postinstall.in revision 1.62 1 #!/bin/sh
2 #
3 # $NetBSD: postinstall.in,v 1.62 2024/03/10 18:23:18 rillig Exp $
4 #
5 # Copyright (c) 2002-2022 The NetBSD Foundation, Inc.
6 # All rights reserved.
7 #
8 # This code is derived from software contributed to The NetBSD Foundation
9 # by Luke Mewburn.
10 #
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions
13 # are met:
14 # 1. Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 # 2. Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 #
32 # postinstall
33 # Check for or fix configuration changes that occur
34 # over time as NetBSD evolves.
35 #
36
37 #
38 # NOTE: Be sure to use ${DEST_DIR} prefix before all real file operations.
39 #
40
41 #
42 # checks to add:
43 # - sysctl(8) renames (net.inet6.ip6.bindv6only -> net.inet6.ip6.v6only)
44 # - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*, ...) ?
45 # - support quiet/verbose mode ?
46 # - differentiate between failures caused by missing source
47 # and real failures
48 # - install moduli into usr/share/examples/ssh and use from there?
49 # - differentiate between "needs fix" versus "can't fix" issues
50 #
51
52 # This script is executed as part of a cross build. Allow the build
53 # environment to override the locations of some tools.
54 : ${AWK:=awk}
55 : ${DB:=db}
56 : ${GREP:=grep}
57 : ${HOST_SH:=sh}
58 : ${MAKE:=make}
59 : ${PWD_MKDB:=/usr/sbin/pwd_mkdb}
60 : ${SED:=sed}
61 : ${SORT:=sort}
62 : ${STAT:=stat}
63 : ${RM:=rm}
64
65 #
66 # helper functions
67 #
68
69 err()
70 {
71 local exitval=$1
72 shift
73 echo 1>&2 "${PROGNAME}: $*"
74 if [ -n "${SCRATCHDIR}" ]; then
75 ${RM} -rf "${SCRATCHDIR}"
76 fi
77 exit ${exitval}
78 }
79
80 warn()
81 {
82 echo 1>&2 "${PROGNAME}: $*"
83 }
84
85 msg()
86 {
87 echo " $*"
88 }
89
90 mkdtemp()
91 {
92 # Make sure we don't loop forever if mkdir will always fail.
93 [ -d /tmp ] || err 2 /tmp is not a directory
94 [ -w /tmp ] || err 2 /tmp is not writable
95
96 local base="/tmp/_postinstall.$$"
97 local serial=0
98 local dir
99
100 while true; do
101 dir="${base}.${serial}"
102 mkdir -m 0700 "${dir}" && break
103 serial=$((${serial} + 1))
104 done
105 echo "${dir}"
106 }
107
108 # Quote args to make them safe in the shell.
109 # Usage: quotedlist="$(shell_quote args...)"
110 #
111 # After building up a quoted list, use it by evaling it inside
112 # double quotes, like this:
113 # eval "set -- $quotedlist"
114 # or like this:
115 # eval "\$command $quotedlist \$filename"
116 #
117 shell_quote()
118 {(
119 local result=''
120 local arg qarg
121 LC_COLLATE=C ; export LC_COLLATE # so [a-zA-Z0-9] works in ASCII
122 for arg in "$@" ; do
123 case "${arg}" in
124 '')
125 qarg="''"
126 ;;
127 *[!-./a-zA-Z0-9]*)
128 # Convert each embedded ' to '\'',
129 # then insert ' at the beginning of the first line,
130 # and append ' at the end of the last line.
131 # Finally, elide unnecessary '' pairs at the
132 # beginning and end of the result and as part of
133 # '\'''\'' sequences that result from multiple
134 # adjacent quotes in he input.
135 qarg="$(printf "%s\n" "$arg" | \
136 ${SED} -e "s/'/'\\\\''/g" \
137 -e "1s/^/'/" -e "\$s/\$/'/" \
138 -e "1s/^''//" -e "\$s/''\$//" \
139 -e "s/'''/'/g"
140 )"
141 ;;
142 *)
143 # Arg is not the empty string, and does not contain
144 # any unsafe characters. Leave it unchanged for
145 # readability.
146 qarg="${arg}"
147 ;;
148 esac
149 result="${result}${result:+ }${qarg}"
150 done
151 printf "%s\n" "$result"
152 )}
153
154 # Convert arg $1 to a basic regular expression (as in sed)
155 # that will match the arg. This works by inserting backslashes
156 # before characters that are special in basic regular expressions.
157 # It also inserts backslashes before the extra characters specified
158 # in $2 (which defaults to "/,").
159 # XXX: Does not handle embedded newlines.
160 # Usage: regex="$(bre_quote "${string}")"
161 bre_quote()
162 {
163 local arg="$1"
164 local extra="${2-/,}"
165 printf "%s\n" "${arg}" | ${SED} -e 's/[][^$.*\\'"${extra}"']/\\&/g'
166 }
167
168 # unprefix dir
169 # Remove any dir prefix from a list of paths on stdin,
170 # and write the result to stdout. Useful for converting
171 # from ${DEST_DIR}/path to /path.
172 #
173 unprefix()
174 {
175 [ $# -eq 1 ] || err 3 "USAGE: unprefix dir"
176 local prefix="${1%/}"
177 prefix="$(bre_quote "${prefix}")"
178
179 ${SED} -e "s,^${prefix}/,/,"
180 }
181
182 # additem item description
183 # Add item to list of supported items to check/fix,
184 # which are checked/fixed by default if no item is requested by user.
185 #
186 additem()
187 {
188 [ $# -eq 2 ] || err 3 "USAGE: additem item description"
189 defaultitems="${defaultitems}${defaultitems:+ }$1"
190 eval desc_$1=\"\$2\"
191 }
192
193 # adddisableditem item description
194 # Add item to list of supported items to check/fix,
195 # but execute the item only if the user asks for it explicitly.
196 #
197 adddisableditem()
198 {
199 [ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
200 otheritems="${otheritems}${otheritems:+ }$1"
201 eval desc_$1=\"\$2\"
202 }
203
204 # checkdir op dir mode
205 # Ensure dir exists, and if not, create it with the appropriate mode.
206 # Returns 0 if ok, 1 otherwise.
207 #
208 check_dir()
209 {
210 [ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
211 local op="$1"
212 local dir="$2"
213 local mode="$3"
214 [ -d "${dir}" ] && return 0
215 if [ "${op}" = "check" ]; then
216 msg "${dir} is not a directory"
217 return 1
218 elif ! mkdir -m "${mode}" "${dir}" ; then
219 msg "Can't create missing ${dir}"
220 return 1
221 else
222 msg "Missing ${dir} created"
223 fi
224 return 0
225 }
226
227 # check_ids op type file srcfile start id ...
228 # Check if file of type "users" or "groups" contains the relevant IDs.
229 # Use srcfile as a reference for the expected contents.
230 # The specified "id" names should be given in numerical order,
231 # with the first name corresponding to numerical value "start",
232 # and with the special name "SKIP" being used to mark gaps in the
233 # sequence.
234 # Returns 0 if ok, 1 otherwise.
235 #
236 check_ids()
237 {
238 [ $# -ge 6 ] || err 3 "USAGE: checks_ids op type file srcfile start id ..."
239 local op="$1"
240 local type="$2"
241 local file="$3"
242 local srcfile="$4"
243 local start="$5"
244 shift 5
245 #local ids="$@"
246
247 if [ ! -f "${file}" ]; then
248 msg "${file} doesn't exist; can't check for missing ${type}"
249 return 1
250 fi
251 if [ ! -r "${file}" ]; then
252 msg "${file} is not readable; can't check for missing ${type}"
253 return 1
254 fi
255 local notfixed=""
256 if [ "${op}" = "fix" ]; then
257 notfixed="${NOT_FIXED}"
258 fi
259 local missing="$(${AWK} -v start=$start -F: '
260 BEGIN {
261 for (x = 1; x < ARGC; x++) {
262 if (ARGV[x] == "SKIP")
263 continue;
264 idlist[ARGV[x]]++;
265 value[ARGV[x]] = start + x - 1;
266 }
267 ARGC=1
268 }
269 {
270 found[$1]++
271 number[$1] = $3
272 }
273 END {
274 for (id in idlist) {
275 if (!(id in found))
276 printf("%s (missing)\n", id)
277 else if (number[id] != value[id])
278 printf("%s (%d != %d)\n", id,
279 number[id], value[id])
280 start++;
281 }
282 }
283 ' "$@" < "${file}")" || return 1
284 if [ -n "${missing}" ]; then
285 msg "Error ${type}${notfixed}:" $(echo ${missing})
286 msg "Use the following as a template:"
287 set -- ${missing}
288 while [ $# -gt 0 ]
289 do
290 ${GREP} -E "^${1}:" ${srcfile}
291 shift 2
292 done | sort -t: -k3n
293 msg "and adjust if necessary."
294 return 1
295 fi
296 return 0
297 }
298
299 # populate_dir op onlynew src dst mode file ...
300 # Perform op ("check" or "fix") on files in src/ against dst/
301 # If op = "check" display missing or changed files, optionally with diffs.
302 # If op != "check" copies any missing or changed files.
303 # If onlynew evaluates to true, changed files are ignored.
304 # Returns 0 if ok, 1 otherwise.
305 #
306 populate_dir()
307 {
308 [ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dst mode file ..."
309 local op="$1"
310 local onlynew="$2"
311 local src="$3"
312 local dst="$4"
313 local mode="$5"
314 shift 5
315 #local files="$@"
316
317 if [ ! -d "${src}" ]; then
318 msg "${src} is not a directory; skipping check"
319 return 1
320 fi
321 check_dir "${op}" "${dst}" 755 || return 1
322
323 local cmpdir_rv=0
324 local f fs fd error
325 for f in "$@"; do
326 fs="${src}/${f}"
327 fd="${dst}/${f}"
328 error=""
329 if [ ! -f "${fd}" ]; then
330 error="${fd} does not exist"
331 elif ! cmp -s "${fs}" "${fd}" ; then
332 if $onlynew; then # leave existing ${fd} alone
333 continue;
334 fi
335 error="${fs} != ${fd}"
336 else
337 continue
338 fi
339 if [ "${op}" = "check" ]; then
340 msg "${error}"
341 if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
342 diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
343 fi
344 cmpdir_rv=1
345 elif ! ${RM} -f "${fd}" ||
346 ! cp -f "${fs}" "${fd}"; then
347 msg "Can't copy ${fs} to ${fd}"
348 cmpdir_rv=1
349 elif ! chmod "${mode}" "${fd}"; then
350 msg "Can't change mode of ${fd} to ${mode}"
351 cmpdir_rv=1
352 else
353 msg "Copied ${fs} to ${fd}"
354 fi
355 done
356 return ${cmpdir_rv}
357 }
358
359 # compare_dir op src dst mode file ...
360 # Perform op ("check" or "fix") on files in src/ against dst/
361 # If op = "check" display missing or changed files, optionally with diffs.
362 # If op != "check" copies any missing or changed files.
363 # Returns 0 if ok, 1 otherwise.
364 #
365 compare_dir()
366 {
367 [ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dst mode file ..."
368 local op="$1"
369 local src="$2"
370 local dst="$3"
371 local mode="$4"
372 shift 4
373 #local files="$@"
374
375 populate_dir "$op" false "$src" "$dst" "$mode" "$@"
376 }
377
378 # move_file op src dst --
379 # Check (op == "check") or move (op != "check") from src to dst.
380 # Returns 0 if ok, 1 otherwise.
381 #
382 move_file()
383 {
384 [ $# -eq 3 ] || err 3 "USAGE: move_file op src dst"
385 local op="$1"
386 local src="$2"
387 local dst="$3"
388
389 if [ -f "${src}" -a ! -f "${dst}" ]; then
390 if [ "${op}" = "check" ]; then
391 msg "Move ${src} to ${dst}"
392 return 1
393 fi
394 if ! mv "${src}" "${dst}"; then
395 msg "Can't move ${src} to ${dst}"
396 return 1
397 fi
398 msg "Moved ${src} to ${dst}"
399 fi
400 return 0
401 }
402
403 # rcconf_is_set op name var [verbose] --
404 # Load the rcconf for name, and check if obsolete rc.conf(5) variable
405 # var is defined or not.
406 # Returns 0 if defined (even to ""), otherwise 1.
407 # If verbose != "", print an obsolete warning if the var is defined.
408 #
409 rcconf_is_set()
410 {
411 [ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
412 local op="$1"
413 local name="$2"
414 local var="$3"
415 local verbose="$4"
416 local notfixed=""
417 if [ "${op}" = "fix" ]; then
418 notfixed="${NOT_FIXED}"
419 fi
420 (
421 for f in \
422 "${DEST_DIR}/etc/rc.conf" \
423 "${DEST_DIR}/etc/rc.conf.d/${name}"; do
424 [ -f "${f}" ] && . "${f}"
425 done
426 eval echo -n \"\${${var}}\" 1>&3
427 if eval "[ -n \"\${${var}+SET}\" ]"; then
428 if [ -n "${verbose}" ]; then
429 msg \
430 "Obsolete rc.conf(5) variable '\$${var}' found.${notfixed}"
431 fi
432 exit 0
433 else
434 exit 1
435 fi
436 )
437 }
438
439 # rcvar_is_enabled var
440 # Check if rcvar is enabled
441 #
442 rcvar_is_enabled()
443 {
444 [ $# -eq 1 ] || err 3 "USAGE: rcvar_is_enabled var"
445 local var="$1"
446 (
447 [ -f "${DEST_DIR}/etc/rc.conf" ] && . "${DEST_DIR}/etc/rc.conf"
448 eval val=\"\${${var}}\"
449 case $val in
450 # "yes", "true", "on", or "1"
451 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
452 exit 0
453 ;;
454
455 *)
456 exit 1
457 ;;
458 esac
459 )
460 }
461
462 # find_file_in_dirlist() file message dir1 ... --
463 # Find which directory file is in, and sets ${dir} to match.
464 # Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
465 #
466 # Generally, check the directory for the "checking from source" case,
467 # and then the directory for the "checking from extracted etc.tgz" case.
468 #
469 find_file_in_dirlist()
470 {
471 [ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 ..."
472
473 local file="$1" ; shift
474 local msg="$1" ; shift
475 local dir1st= # first dir in list
476 # returns dir
477 for dir in "$@"; do
478 : ${dir1st:="${dir}"}
479 if [ -f "${dir}/${file}" ]; then
480 if [ "${dir1st}" != "${dir}" ]; then
481 msg \
482 "(Checking for ${msg} from ${dir} instead of ${dir1st})"
483 fi
484 return 0
485 fi
486 done
487 msg "Can't find source directory for ${msg}"
488 return 1
489 }
490
491 # file_exists_exact path
492 # Returns true if a file exists in the ${DEST_DIR} whose name
493 # is exactly ${path}, interpreted in a case-sensitive way
494 # even if the underlying file system is case-insensitive.
495 #
496 # The path must begin with '/' or './', and is interpreted as
497 # being relative to ${DEST_DIR}.
498 #
499 file_exists_exact()
500 {
501 [ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
502 local path="${1#.}"
503 [ -h "${DEST_DIR}${path}" ] || \
504 [ -e "${DEST_DIR}${path}" ] || return 1
505 while [ "${path}" != "/" -a "${path}" != "." ] ; do
506 local dirname="$(dirname "${path}" 2>/dev/null)"
507 local basename="$(basename "${path}" 2>/dev/null)"
508 ls -fa "${DEST_DIR}${dirname}" 2> /dev/null \
509 | ${GREP} -q -F -x "${basename}" \
510 || return 1
511 path="${dirname}"
512 done
513 return 0
514 }
515
516 # obsolete_paths op
517 # Obsolete the list of paths provided on stdin.
518 # Each path should start with '/' or './', and
519 # will be interpreted relative to ${DEST_DIR}.
520 #
521 obsolete_paths()
522 {
523 [ -n "$1" ] || err 3 "USAGE: obsolete_paths fix|check"
524 local op="$1"
525 local failed=0
526 local ofile cmd ftype
527
528 while read ofile; do
529 if ! ${file_exists_exact} "${ofile}"; then
530 continue
531 fi
532 ofile="${DEST_DIR}${ofile#.}"
533 cmd="${RM}"
534 ftype="file"
535 if [ -h "${ofile}" ]; then
536 ftype="link"
537 elif [ -d "${ofile}" ]; then
538 ftype="directory"
539 cmd="rmdir"
540 elif [ ! -e "${ofile}" ]; then
541 continue
542 fi
543 if [ "${op}" = "check" ]; then
544 msg "Remove obsolete ${ftype} ${ofile}"
545 failed=1
546 elif ! eval "${cmd} \"\${ofile}\""; then
547 msg "Can't remove obsolete ${ftype} ${ofile}"
548 failed=1
549 else
550 msg "Removed obsolete ${ftype} ${ofile}"
551 fi
552 done
553 return ${failed}
554 }
555
556 # obsolete_libs dir
557 # Display the minor/teeny shared libraries in dir that are considered
558 # to be obsolete.
559 #
560 # The implementation supports removing obsolete major libraries
561 # if the awk variable AllLibs is set, although there is no way to
562 # enable that in the enclosing shell function as this time.
563 #
564 obsolete_libs()
565 {
566 [ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
567 local dir="$1"
568
569 _obsolete_libs "${dir}"
570 _obsolete_libs "/usr/libdata/debug/${dir}"
571 }
572
573 exclude()
574 {
575 local dollar
576 case "$1" in
577 -t)
578 dollar='$'
579 shift
580 ;;
581 *)
582 dollar=
583 ;;
584 esac
585 if [ -z "$*" ]; then
586 cat
587 else
588 eval ${GREP} -v -E "'(^$(echo $* | \
589 ${SED} -e s/\\./\\\\./g -e 's/ /'${dollar}'|^/'g)${dollar})'"
590 fi
591 }
592
593 #
594 # find all the 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]lack' "${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/blacklist.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/black/block/)
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-scale-bitmap-fonts.conf
1339 10-sub-pixel-bgr.conf
1340 10-sub-pixel-none.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 manualmsg
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 manualmsg="[existing /etc/openssl/certs configuration"
1752 manualmsg="$manualmsg detected by postinstall(8)]"
1753 # Change the commented-out `#manual' line to
1754 # uncommented `manual', or print an error
1755 # message if there is no `#manual' line and put
1756 # `manual' at the end.
1757 awk -v defaultconf="$defaultconf" \
1758 -v manualmsg="$manualmsg" '
1759 BEGIN {
1760 manual = 0
1761 }
1762 /^#manual/ && !manual {
1763 manual = 1
1764 sub(/^#/, "")
1765 print
1766 print "#", manualmsg
1767 next
1768 }
1769 {
1770 print
1771 }
1772 END {
1773 if (!manual) {
1774 printf "warning: %s %s?\n", \
1775 "corrupt", defaultconf \
1776 >"/dev/stderr"
1777 print "manual"
1778 print "#", manualmsg
1779 }
1780 }
1781 ' <$defaultconf >${certsconf}.tmp
1782 else
1783 msg "installing default /etc/openssl/certs.conf"
1784 cat <$defaultconf >${certsconf}.tmp
1785 fi && mv -f -- "${certsconf}.tmp" "$certsconf"
1786 ;;
1787 *) err 3 "USAGE: do_opensslcerts fix|check"
1788 ;;
1789 esac
1790 }
1791
1792
1793 #
1794 # opensslcertsrehash
1795 #
1796
1797 additem opensslcertsrehash "make /etc/openssl/certs cache of TLS trust anchors"
1798 do_opensslcertsrehash()
1799 {
1800 local mtreekeys scratchdir
1801
1802 [ -n "$1" ] || err 3 "USAGE: do_opensslcertsrehash fix|check"
1803
1804 if [ ! -r "${DEST_DIR}/etc/openssl/certs.conf" ]; then
1805 msg "/etc/openssl/certs.conf missing; see certctl(8)"
1806 return 1
1807 fi
1808
1809 case $1 in
1810 check) # Create a scratch rehash for comparison.
1811 mtreekeys="type,link"
1812 scratchdir="${SCRATCHDIR}/opensslcerts"
1813 /usr/sbin/certctl -c "$scratchdir" rehash || return $?
1814
1815 # This will create ${scratchdir}/.certctl unless the
1816 # configuration is manual. If the configuration is
1817 # manual, stop here; nothing to do. certctl(8) will
1818 # have already printed a message about that.
1819 #
1820 # XXX Grody to rely on the internal structure used by
1821 # certctl(8), but less bad than having two versions of
1822 # the config parsing logic.
1823 if [ ! -f "${scratchdir}/.certctl" ]; then
1824 return 0
1825 fi
1826
1827 # Do a dry run of rehashing into the real
1828 # /etc/openssl/certs. This curious extra step ensures
1829 # that we report a failure if /etc/openssl/certs
1830 # appears to be managed manually, but `manual' was not
1831 # specified in /etc/openssl/certs.conf.
1832 /usr/sbin/certctl -n rehash || return $?
1833
1834 # Compare the trees with mtree(8). Inconveniently,
1835 # mtree returns status zero even if there are missing
1836 # or extra files. So instead of examining the return
1837 # status, test for any output. Empty output means
1838 # everything matches; otherwise the mismatch, missing,
1839 # or extra files are output.
1840 mtree -p "$scratchdir" -c -k "$mtreekeys" \
1841 | mtree -p "${DEST_DIR}/etc/openssl/certs" 2>&1 \
1842 | {
1843 while read -r line; do
1844 # mismatch, missing, or extra
1845 msg "/etc/openssl/certs needs rehash"
1846 exit 1
1847 done
1848 exit 0
1849 }
1850 ;;
1851 fix) # This runs openssl(1), which is not available as a
1852 # build-time tool. So for now, restrict it to running
1853 # on the installed system.
1854 case $DEST_DIR in
1855 ''|/) ;;
1856 *) msg "opensslcertsrehash limited to DEST_DIR=/"
1857 return 1
1858 ;;
1859 esac
1860 /usr/sbin/certctl rehash
1861 ;;
1862 *) err 3 "USAGE: do_opensslcerts fix|check"
1863 ;;
1864 esac
1865 }
1866
1867
1868 #
1869 # pam
1870 #
1871
1872 additem pam "/etc/pam.d is populated"
1873 do_pam()
1874 {
1875 [ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
1876 local op="$1"
1877 local failed=0
1878
1879 populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
1880 "${DEST_DIR}/etc/pam.d" 644 \
1881 README cron display_manager ftpd gdm imap kde login other \
1882 passwd pop3 ppp racoon rexecd rsh sshd su system telnetd \
1883 xdm xserver
1884
1885 failed=$(( ${failed} + $? ))
1886
1887 return ${failed}
1888 }
1889
1890
1891 #
1892 # periodic
1893 #
1894
1895 additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
1896 do_periodic()
1897 {
1898 [ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
1899
1900 compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1901 daily weekly monthly security
1902 }
1903
1904
1905 #
1906 # pf
1907 #
1908
1909 additem pf "pf configuration being up to date"
1910 do_pf()
1911 {
1912 [ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
1913 local op="$1"
1914 local failed=0
1915
1916 find_file_in_dirlist pf.os "pf.os" \
1917 "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
1918 || return 1
1919 # ${dir} is set by find_file_in_dirlist()
1920 populate_dir "${op}" true \
1921 "${dir}" "${DEST_DIR}/etc" 644 \
1922 pf.conf
1923 failed=$(( ${failed} + $? ))
1924
1925 compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
1926 failed=$(( ${failed} + $? ))
1927
1928 return ${failed}
1929 }
1930
1931
1932 #
1933 # ptyfsoldnodes
1934 #
1935
1936 additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
1937 do_ptyfsoldnodes()
1938 {
1939 [ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes fix|check"
1940 local op="$1"
1941
1942 # Check whether ptyfs is in use
1943 local failed=0;
1944 if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
1945 msg "ptyfs is not in use"
1946 return 0
1947 fi
1948
1949 if [ ! -e "${DEST_DIR}/dev/pts" ]; then
1950 msg "ptyfs is not properly configured: missing /dev/pts"
1951 return 1
1952 fi
1953
1954 # Find the device major numbers for the pty master and slave
1955 # devices, by parsing the output from "MAKEDEV -s pty0".
1956 #
1957 # Output from MAKEDEV looks like this:
1958 # ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
1959 # ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
1960 #
1961 # Output from awk, used in the eval statement, looks like this:
1962 # maj_ptym=6; maj_ptys=5;
1963 #
1964 local maj_ptym maj_ptys
1965 find_makedev
1966 eval "$(
1967 ${HOST_SH} "${MAKEDEV_DIR}/MAKEDEV" -s pty0 2>/dev/null \
1968 | ${AWK} '\
1969 BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
1970 /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
1971 maj_ptym = gensub(after_re, "", 1, maj_ptym); }
1972 /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
1973 maj_ptys = gensub(after_re, "", 1, maj_ptys); }
1974 END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
1975 '
1976 )"
1977 #msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
1978 if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
1979 msg "Cannot find device major numbers for pty master and slave"
1980 return 1
1981 fi
1982
1983 # look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
1984 # have the expected device major numbers. ttyv* is typically not a
1985 # pty device, but we check it anyway.
1986 #
1987 # The "for d1" loop is intended to avoid overflowing ARG_MAX;
1988 # otherwise we could have used a single glob pattern.
1989 #
1990 # If there are no files that match a particular pattern,
1991 # then stat prints something like:
1992 # stat: /dev/[pt]tyx?: lstat: No such file or directory
1993 # and we ignore it. XXX: We also ignore other error messages.
1994 #
1995 local d1 major node
1996 local tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
1997
1998 for d1 in p q r s t u v w x y z P Q R S T; do
1999 ${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
2000 done \
2001 | while read -r major node ; do
2002 case "$major" in
2003 ${maj_ptym}|${maj_ptys}) echo "$node" ;;
2004 esac
2005 done > "${tmp}"
2006
2007 local desc="legacy device node"
2008 while read node; do
2009 if [ "${op}" = "check" ]; then
2010 msg "Remove ${desc} ${node}"
2011 failed=1
2012 else # "fix"
2013 if ${RM} "${node}"; then
2014 msg "Removed ${desc} ${node}"
2015 else
2016 warn "Failed to remove ${desc} ${node}"
2017 failed=1
2018 fi
2019 fi
2020 done < "${tmp}"
2021 ${RM} "${tmp}"
2022
2023 return ${failed}
2024 }
2025
2026
2027 #
2028 # pwd_mkdb
2029 #
2030
2031 additem pwd_mkdb "passwd database version"
2032 do_pwd_mkdb()
2033 {
2034 [ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb fix|check"
2035 local op="$1"
2036 local failed=0
2037
2038 # XXX Ideally, we should figure out the endianness of the
2039 # target machine, and add "-E B"/"-E L" to the db(1) flags,
2040 # and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
2041 # the same as the host machine. It probably doesn't matter,
2042 # because we don't expect "postinstall fix pwd_mkdb" to be
2043 # invoked during a cross build.
2044
2045 set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
2046 'VERSION\0')
2047 case "$2" in
2048 '\001\000\000\000') return 0 ;; # version 1, little-endian
2049 '\000\000\000\001') return 0 ;; # version 1, big-endian
2050 esac
2051
2052 if [ "${op}" = "check" ]; then
2053 msg "Update format of passwd database"
2054 failed=1
2055 elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
2056 "${DEST_DIR}/etc/master.passwd";
2057 then
2058 msg "Can't update format of passwd database"
2059 failed=1
2060 else
2061 msg "Updated format of passwd database"
2062 fi
2063
2064 return ${failed}
2065 }
2066
2067
2068 #
2069 # rc
2070 #
2071
2072 # There is no info in src/distrib or /etc/mtree which rc* files
2073 # can be overwritten unconditionally on upgrade. See PR/54741.
2074 rc_644_files="
2075 rc
2076 rc.subr
2077 rc.shutdown
2078 "
2079
2080 rc_obsolete_vars="
2081 amd amd_master
2082 btcontrol btcontrol_devices
2083 critical_filesystems critical_filesystems_beforenet
2084 mountcritlocal mountcritremote
2085 network ip6forwarding
2086 network nfsiod_flags
2087 sdpd sdpd_control
2088 sdpd sdpd_groupname
2089 sdpd sdpd_username
2090 sysctl defcorename
2091 "
2092
2093 update_rc()
2094 {
2095 local op=$1
2096 local dir=$2
2097 local name=$3
2098 local bindir=$4
2099 local rcdir=$5
2100
2101 if [ ! -x "${DEST_DIR}/${bindir}/${name}" ]; then
2102 return 0
2103 fi
2104
2105 if ! find_file_in_dirlist "${name}" "${name}" \
2106 "${rcdir}" "${SRC_DIR}/etc/rc.d"; then
2107 return 1
2108 fi
2109 populate_dir "${op}" false "${dir}" "${DEST_DIR}/etc/rc.d" 555 "${name}"
2110 return $?
2111 }
2112
2113 # select non-obsolete files in a sets file
2114 # $1: directory pattern
2115 # $2: file pattern
2116 # $3: filename
2117 select_set_files()
2118 {
2119 local qdir="$(echo $1 | ${SED} -e s@/@\\\\/@g -e s/\\./\\\\./g)"
2120 ${SED} -n -e /obsolete/d \
2121 -e "/^\.${qdir}/s@^.$2[[:space:]].*@\1@p" $3
2122 }
2123
2124 # select obsolete files in a sets file
2125 # $1: directory pattern
2126 # $2: file pattern
2127 # $3: setname
2128 select_obsolete_files()
2129 {
2130 if $SOURCEMODE; then
2131 ${SED} -n -e "/obsolete/s@\.$1$2[[:space:]].*@\1@p" \
2132 "${SRC_DIR}/distrib/sets/lists/$3/mi"
2133 return
2134 fi
2135
2136 # On upgrade builds we don't extract the "etc" set so we
2137 # try to use the source set instead. See PR/54730 for
2138 # ways to better handle this.
2139
2140 local obsolete_dir
2141
2142 if [ $3 = "etc" ] ;then
2143 obsolete_dir="${SRC_DIR}/var/db/obsolete"
2144 else
2145 obsolete_dir="${DEST_DIR}/var/db/obsolete"
2146 fi
2147 ${SED} -n -e "s@\.$1$2\$@\1@p" "${obsolete_dir}/$3"
2148 }
2149
2150 getetcsets()
2151 {
2152 if $SOURCEMODE; then
2153 echo "${SRC_DIR}/distrib/sets/lists/etc/mi"
2154 else
2155 echo "${SRC_DIR}/etc/mtree/set.etc"
2156 fi
2157 }
2158
2159 additem rc "/etc/rc* and /etc/rc.d/ being up to date"
2160 do_rc()
2161 {
2162 [ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
2163 local op="$1"
2164 local failed=0
2165 local generated_scripts=""
2166 local etcsets=$(getetcsets)
2167 if [ "${MKX11}" != "no" ]; then
2168 generated_scripts="${generated_scripts} xdm xfs"
2169 fi
2170
2171 # Directories of external programs that have rc files (in bsd)
2172 local rc_external_files="blocklist nsd unbound"
2173
2174 # rc* files in /etc/
2175 # XXX: at least rc.conf and rc.local shouldn't be updated. PR/54741
2176 #local rc_644_files="$(select_set_files /etc/rc \
2177 # "/etc/\(rc[^[:space:]/]*\)" ${etcsets})"
2178
2179 # no-obsolete rc files in /etc/rc.d
2180 local rc_555_files="$(select_set_files /etc/rc.d/ \
2181 "/etc/rc\.d/\([^[:space:]]*\)" ${etcsets} | \
2182 exclude ${rc_external_files})"
2183
2184 # obsolete rc file in /etc/rc.d
2185 local rc_obsolete_files="$(select_obsolete_files /etc/rc.d/ \
2186 "\([^[:space:]]*\)" etc)"
2187
2188 compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
2189 ${rc_644_files}
2190 failed=$(( ${failed} + $? ))
2191
2192 local extra_scripts
2193 if ! $SOURCEMODE; then
2194 extra_scripts="${generated_scripts}"
2195 else
2196 extra_scripts=""
2197 fi
2198
2199 compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
2200 ${rc_555_files} \
2201 ${extra_scripts}
2202 failed=$(( ${failed} + $? ))
2203
2204 local i rc_file
2205 for i in ${rc_external_files}; do
2206 case $i in
2207 *d) rc_file=${i};;
2208 *) rc_file=${i}d;;
2209 esac
2210
2211 update_rc "${op}" "${dir}" ${rc_file} /sbin \
2212 "${SRC_DIR}/external/bsd/$i/etc/rc.d"
2213 failed=$(( ${failed} + $? ))
2214 done
2215
2216 if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
2217 # generate scripts
2218 mkdir "${SCRATCHDIR}/rc"
2219 for f in ${generated_scripts}; do
2220 ${SED} -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
2221 < "${SRC_DIR}/etc/rc.d/${f}.in" \
2222 > "${SCRATCHDIR}/rc/${f}"
2223 done
2224 compare_dir "${op}" "${SCRATCHDIR}/rc" \
2225 "${DEST_DIR}/etc/rc.d" 555 \
2226 ${generated_scripts}
2227 failed=$(( ${failed} + $? ))
2228 fi
2229
2230 # check for obsolete rc.d files
2231 for f in ${rc_obsolete_files}; do
2232 local fd="/etc/rc.d/${f}"
2233 [ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
2234 done | obsolete_paths "${op}"
2235 failed=$(( ${failed} + $? ))
2236
2237 # check for obsolete rc.conf(5) variables
2238 set -- ${rc_obsolete_vars}
2239 while [ $# -gt 1 ]; do
2240 if rcconf_is_set "${op}" "$1" "$2" 1; then
2241 failed=1
2242 fi
2243 shift 2
2244 done
2245
2246 return ${failed}
2247 }
2248
2249
2250 #
2251 # sendmail
2252 #
2253
2254 adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
2255 do_sendmail()
2256 {
2257 [ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
2258 op="$1"
2259 failed=0
2260
2261 # Don't complain if the "sendmail" package is installed because the
2262 # files might still be in use.
2263 if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
2264 return 0
2265 fi
2266
2267 for f in /etc/mail/helpfile /etc/mail/local-host-names \
2268 /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
2269 /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
2270 $( ( find "${DEST_DIR}/usr/share/sendmail" -type f ; \
2271 find "${DEST_DIR}/usr/share/sendmail" -type d \
2272 ) | unprefix "${DEST_DIR}" ) \
2273 /var/log/sendmail.st \
2274 /var/spool/clientmqueue \
2275 /var/spool/mqueue
2276 do
2277 [ -e "${DEST_DIR}${f}" ] && echo "${f}"
2278 done | obsolete_paths "${op}"
2279 failed=$(( ${failed} + $? ))
2280
2281 return ${failed}
2282 }
2283
2284
2285 #
2286 # ssh
2287 #
2288
2289 additem ssh "ssh configuration update"
2290 do_ssh()
2291 {
2292 [ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
2293 local op="$1"
2294
2295 local failed=0
2296 local etcssh="${DEST_DIR}/etc/ssh"
2297 if ! check_dir "${op}" "${etcssh}" 755; then
2298 failed=1
2299 fi
2300
2301 if [ ${failed} -eq 0 ]; then
2302 for f in \
2303 ssh_known_hosts ssh_known_hosts2 \
2304 ssh_host_dsa_key ssh_host_dsa_key.pub \
2305 ssh_host_rsa_key ssh_host_rsa_key.pub \
2306 ssh_host_key ssh_host_key.pub \
2307 ; do
2308 if ! move_file "${op}" \
2309 "${DEST_DIR}/etc/${f}" "${etcssh}/${f}" ; then
2310 failed=1
2311 fi
2312 done
2313 for f in sshd.conf ssh.conf ; do
2314 # /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
2315 #
2316 if ! move_file "${op}" \
2317 "${etcssh}/${f}" "${etcssh}/${f%.conf}_config" ;
2318 then
2319 failed=1
2320 fi
2321 # /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
2322 #
2323 if ! move_file "${op}" \
2324 "${DEST_DIR}/etc/${f}" \
2325 "${etcssh}/${f%.conf}_config" ;
2326 then
2327 failed=1
2328 fi
2329 done
2330 fi
2331
2332 local sshdconf=""
2333 local f
2334 for f in \
2335 "${etcssh}/sshd_config" \
2336 "${etcssh}/sshd.conf" \
2337 "${DEST_DIR}/etc/sshd.conf" ; do
2338 if [ -f "${f}" ]; then
2339 sshdconf="${f}"
2340 break
2341 fi
2342 done
2343 if [ -n "${sshdconf}" ]; then
2344 modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
2345 /^[^#$]/ {
2346 kw = tolower($1)
2347 if (kw == "hostkey" &&
2348 $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
2349 sub(/\/etc\/+/, "/etc/ssh/")
2350 }
2351 if (kw == "rhostsauthentication" ||
2352 kw == "verifyreversemapping" ||
2353 kw == "reversemappingcheck") {
2354 sub(/^/, "# DEPRECATED:\t")
2355 }
2356 }
2357 { print }
2358 '
2359 failed=$(( ${failed} + $? ))
2360 fi
2361
2362 if ! find_file_in_dirlist moduli "moduli" \
2363 "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
2364 failed=1
2365 # ${dir} is set by find_file_in_dirlist()
2366 elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
2367 failed=1
2368 fi
2369
2370 if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
2371 failed=1
2372 fi
2373
2374 if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
2375 failed=1
2376 fi
2377
2378 return ${failed}
2379 }
2380
2381
2382 #
2383 # tcpdumpchroot
2384 #
2385
2386 additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
2387 do_tcpdumpchroot()
2388 {
2389 [ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot fix|check"
2390 local op="$1"
2391
2392 local failed=0;
2393 if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
2394 if [ "${op}" = "fix" ]; then
2395 ${RM} "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
2396 failed=$(( ${failed} + $? ))
2397 rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
2398 failed=$(( ${failed} + $? ))
2399 else
2400 failed=1
2401 fi
2402 fi
2403 return ${failed}
2404 }
2405
2406
2407 #
2408 # uid
2409 #
2410
2411 additem uid "required users in /etc/master.passwd"
2412 do_uid()
2413 {
2414 [ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
2415
2416 check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
2417 "${SRC_DIR}/etc/master.passwd" 12 \
2418 postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
2419 _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd \
2420 SKIP _unbound _nsd SKIP _dhcpcd
2421 }
2422
2423
2424 #
2425 # varrwho
2426 #
2427
2428 additem varrwho "required ownership of files in /var/rwho"
2429 do_varrwho()
2430 {
2431 [ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
2432
2433 contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
2434 }
2435
2436
2437 #
2438 # varshm
2439 #
2440
2441 additem varshm "check for a tmpfs mounted on /var/shm"
2442 do_varshm()
2443 {
2444 [ -n "$1" ] || err 3 "USAGE: do_varshm fix|check"
2445 op="$1"
2446 failed=0
2447
2448 [ -f "${DEST_DIR}/etc/fstab" ] || return 0
2449 if ${GREP} -E "^var_shm_symlink" "${DEST_DIR}/etc/rc.conf" >/dev/null 2>&1;
2450 then
2451 failed=0;
2452 elif ${GREP} -w "/var/shm" "${DEST_DIR}/etc/fstab" >/dev/null 2>&1;
2453 then
2454 failed=0;
2455 else
2456 if [ "${op}" = "check" ]; then
2457 failed=1
2458 msg "No /var/shm mount found in ${DEST_DIR}/etc/fstab"
2459 elif [ "${op}" = "fix" ]; then
2460 printf '\ntmpfs\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n' \
2461 >> "${DEST_DIR}/etc/fstab"
2462 msg "Added tmpfs with 25% ram limit as /var/shm"
2463
2464 fi
2465 fi
2466
2467 return ${failed}
2468 }
2469
2470
2471 #
2472 # wscons
2473 #
2474
2475 additem wscons "wscons configuration file update"
2476 do_wscons()
2477 {
2478 [ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
2479 op="$1"
2480
2481 [ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
2482
2483 failed=0
2484 notfixed=""
2485 if [ "${op}" = "fix" ]; then
2486 notfixed="${NOT_FIXED}"
2487 fi
2488 while read _type _arg1 _rest; do
2489 if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
2490 msg \
2491 "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
2492 failed=1
2493 fi
2494 done < "${DEST_DIR}/etc/wscons.conf"
2495
2496 return ${failed}
2497 }
2498
2499
2500 #
2501 # x11
2502 #
2503
2504 additem x11 "x11 configuration update"
2505 do_x11()
2506 {
2507 [ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
2508 local p="$1"
2509
2510 local failed=0
2511 local etcx11="${DEST_DIR}/etc/X11"
2512 local libx11=""
2513 if [ ! -d "${etcx11}" ]; then
2514 msg "${etcx11} is not a directory; skipping check"
2515 return 0
2516 fi
2517 if [ -d "${DEST_DIR}/usr/X11R6/." ]
2518 then
2519 libx11="${DEST_DIR}/usr/X11R6/lib/X11"
2520 if [ ! -d "${libx11}" ]; then
2521 msg "${libx11} is not a directory; skipping check"
2522 return 0
2523 fi
2524 fi
2525
2526 local notfixed=""
2527 if [ "${op}" = "fix" ]; then
2528 notfixed="${NOT_FIXED}"
2529 fi
2530
2531 local d
2532 # check if /usr/X11R6/lib/X11 needs to migrate to /etc/X11
2533 if [ -n "${libx11}" ]; then
2534 for d in \
2535 fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
2536 ; do
2537 sd="${libx11}/${d}"
2538 ld="/etc/X11/${d}"
2539 td="${DEST_DIR}${ld}"
2540 if [ -h "${sd}" ]; then
2541 continue
2542 elif [ -d "${sd}" ]; then
2543 tdfiles="$(find "${td}" \! -type d)"
2544 if [ -n "${tdfiles}" ]; then
2545 msg "${sd} exists yet ${td} already" \
2546 "contains files${notfixed}"
2547 else
2548 msg "Migrate ${sd} to ${td}${notfixed}"
2549 fi
2550 failed=1
2551 elif [ -e "${sd}" ]; then
2552 msg "Unexpected file ${sd}${notfixed}"
2553 continue
2554 else
2555 continue
2556 fi
2557 done
2558 fi
2559
2560 # check if xdm resources have been updated
2561 if [ -r ${etcx11}/xdm/Xresources ] && \
2562 ! ${GREP} -q 'inpColor:' ${etcx11}/xdm/Xresources; then
2563 msg "Update ${etcx11}/xdm/Xresources${notfixed}"
2564 failed=1
2565 fi
2566
2567 return ${failed}
2568 }
2569
2570
2571 #
2572 # xkb
2573 #
2574 # /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
2575 # to a file on 2009-06-12. Fixing this requires removing the directory
2576 # (which we can do) and re-extracting the xbase set (which we can't do),
2577 # or at least adding that one file (which we may be able to do if X11SRCDIR
2578 # is available).
2579 #
2580
2581 additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
2582 do_xkb()
2583 {
2584 [ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
2585 local op="$1"
2586 local failed=0
2587
2588 local pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
2589 local pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
2590
2591 local filemsg="\
2592 ${pcpath} was a directory, should be a file.
2593 To fix, extract the xbase set again."
2594
2595 local notfixed=""
2596 if [ "${op}" = "fix" ]; then
2597 notfixed="${NOT_FIXED}"
2598 fi
2599
2600 if [ ! -d "${DEST_DIR}${pcpath}" ]; then
2601 return 0
2602 fi
2603
2604 # Delete obsolete files in the directory, and the directory
2605 # itself. If the directory contains unexpected extra files
2606 # then it will not be deleted.
2607 ( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
2608 && ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/xbase \
2609 | ${GREP} -E "^\\.?${pcpath}/" ;
2610 echo "${pcpath}" ) \
2611 | obsolete_paths "${op}"
2612 failed=$(( ${failed} + $? ))
2613
2614 # If the directory was removed above, then try to replace it with
2615 # a file.
2616 if [ -d "${DEST_DIR}${pcpath}" ]; then
2617 msg "${filemsg}${notfixed}"
2618 failed=$(( ${failed} + 1 ))
2619 else
2620 if ! find_file_in_dirlist pc "${pcpath}" \
2621 "${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
2622 then
2623 msg "${filemsg}${notfixed}"
2624 failed=$(( ${failed} + 1 ))
2625 else
2626 # ${dir} is set by find_file_in_dirlist()
2627 populate_dir "${op}" true \
2628 "${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
2629 pc
2630 failed=$(( ${failed} + $? ))
2631 fi
2632 fi
2633
2634 return $failed
2635 }
2636
2637
2638 #
2639 # obsolete_stand
2640 # obsolete_stand_debug
2641 #
2642
2643 obsolete_stand_internal()
2644 {
2645 local prefix="$1"
2646 shift
2647 [ -n "$1" ] || err 3 "USAGE: do_obsolete_stand fix|check"
2648 local op="$1"
2649 local failed=0
2650 local dir
2651
2652 for dir in \
2653 ${prefix}/stand/${MACHINE} \
2654 ${prefix}/stand/${MACHINE}-4xx \
2655 ${prefix}/stand/${MACHINE}-booke \
2656 ${prefix}/stand/${MACHINE}-xen \
2657 ${prefix}/stand/${MACHINE}pae-xen
2658 do
2659 [ -d "${DEST_DIR}${dir}" ] && obsolete_stand "${dir}"
2660 done | obsolete_paths "${op}"
2661 failed=$(( ${failed} + $? ))
2662
2663 return ${failed}
2664 }
2665
2666 adddisableditem obsolete_stand "remove obsolete files from /stand"
2667 do_obsolete_stand()
2668 {
2669 obsolete_stand_internal "" "$@"
2670 return $?
2671 }
2672
2673 adddisableditem obsolete_stand_debug "remove obsolete files from /usr/libdata/debug/stand"
2674 do_obsolete_stand_debug()
2675 {
2676 obsolete_stand_internal "/usr/libdata/debug" "$@"
2677 return $?
2678 }
2679
2680
2681 #
2682 # obsolete
2683 #
2684 # NOTE: This item is last to allow other items to move obsolete files.
2685 #
2686
2687 listarchsubdirs()
2688 {
2689 if ! $SOURCEMODE; then
2690 echo "@ARCHSUBDIRS@"
2691 else
2692 ${SED} -n -e '/ARCHDIR_SUBDIR/s/[[:space:]]//gp' \
2693 "${SRC_DIR}/compat/archdirs.mk"
2694 fi
2695 }
2696
2697 getarchsubdirs()
2698 {
2699 local m
2700 local i
2701
2702 case ${MACHINE_ARCH} in
2703 *arm*|*aarch64*) m=arm;;
2704 x86_64) m=amd64;;
2705 *) m=${MACHINE_ARCH};;
2706 esac
2707
2708 for i in $(listarchsubdirs); do
2709 echo $i
2710 done | ${SORT} -u | ${SED} -n -e "/=${m}/s@.*=${m}/\(.*\)@\1@p"
2711 }
2712
2713 getcompatlibdirs()
2714 {
2715 local i
2716
2717 for i in $(getarchsubdirs); do
2718 if [ -d "${DEST_DIR}/usr/lib/$i" ]; then
2719 echo /usr/lib/$i
2720 fi
2721 done
2722 }
2723
2724 additem obsolete "remove obsolete file sets and minor libraries"
2725 do_obsolete()
2726 {
2727 [ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
2728 local op="$1"
2729 local failed=0
2730 local i
2731
2732 ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
2733 failed=$(( ${failed} + $? ))
2734
2735 (
2736 obsolete_libs /lib
2737 obsolete_libs /usr/lib
2738 obsolete_libs /usr/lib/i18n
2739 obsolete_libs /usr/X11R6/lib
2740 obsolete_libs /usr/X11R7/lib
2741 for i in $(getcompatlibdirs); do
2742 obsolete_libs $i
2743 done
2744 ) | obsolete_paths "${op}"
2745 failed=$(( ${failed} + $? ))
2746
2747 return ${failed}
2748 }
2749
2750
2751 #
2752 # end of items
2753 # ------------
2754 #
2755
2756
2757 help()
2758 {
2759 cat << _USAGE_
2760 Usage: ${PROGNAME} [-a ARCH] [-d DEST_DIR] [-m MACHINE] [-s SRC_ARG] [-x XSRC_DIR] OPERATION ...
2761 ${PROGNAME} -?
2762
2763 Perform post-installation checks and/or fixes on a system's
2764 configuration files.
2765 If no items are provided, a default set of checks or fixes is applied.
2766
2767 Options:
2768 -? Display this help, and exit.
2769 -a ARCH Set \$MACHINE_ARCH to ARCH. [${MACHINE_ARCH}]
2770 -d DEST_DIR Destination directory to check. [${DEST_DIR:-/}]
2771 -m MACHINE Set \$MACHINE to MACHINE. [${MACHINE}]
2772 -s SRC_ARG Location of the source files. This may be any of
2773 the following:
2774 -s SRC_DIR A directory that contains a NetBSD
2775 source tree.
2776 -s TGZ_DIR A directory in which one or both of
2777 "etc.tgz" and "xetc.tgz" have been
2778 extracted.
2779 -s TGZ_FILE A distribution set file such as
2780 "etc.tgz" or "xetc.tgz".
2781 May be specified multipled times.
2782 [${SRC_DIR:-/usr/src}]
2783 -x XSRC_DIR Location of the X11 source files. This must be
2784 a directory that contains a NetBSD xsrc tree.
2785 [${XSRC_DIR:-/usr/src/../xsrc}]
2786
2787 Supported values for OPERATION:
2788 help Display this help, and exit.
2789 list List available items.
2790 check ITEM ... Perform post-installation checks on ITEMs.
2791 diff [-bcenpuw] ITEM ...
2792 Similar to 'check' but also output difference of files,
2793 using diff with the provided options.
2794 fix ITEM ... Apply fixes that 'check' determines need to be applied.
2795 usage Display this help, and exit.
2796 _USAGE_
2797 }
2798
2799 usage()
2800 {
2801 help 1>&2
2802 exit 2
2803 }
2804
2805
2806 list()
2807 {
2808 local i
2809 echo "Default set of items (to apply if no items are provided by user):"
2810 echo " Item Description"
2811 echo " ---- -----------"
2812 for i in ${defaultitems}; do
2813 eval desc=\"\${desc_${i}}\"
2814 printf " %-20s %s\n" "${i}" "${desc}"
2815 done
2816 echo "Items disabled by default (must be requested explicitly):"
2817 echo " Item Description"
2818 echo " ---- -----------"
2819 for i in ${otheritems}; do
2820 eval desc=\"\${desc_${i}}\"
2821 printf " %-20s %s\n" "${i}" "${desc}"
2822 done
2823 }
2824
2825
2826 main()
2827 {
2828 DIRMODE=false # true if "-s" specified a directory
2829 ITEMS= # items to check|diff|fix. [${defaultitems}]
2830 N_SRC_ARGS=0 # number of "-s" args in SRC_ARGLIST
2831 SOURCEMODE=false # true if "-s" specified a source directory
2832 SRC_ARGLIST= # quoted list of one or more "-s" args
2833 SRC_DIR="${SRC_ARG}" # set default value for early usage()
2834 TGZLIST= # quoted list list of tgz files
2835 TGZMODE=false # true if "-s" specifies a tgz file
2836 XSRC_DIR="${SRC_ARG}/../xsrc"
2837 XSRC_DIR_FIX=
2838
2839 case "$(uname -s)" in
2840 Darwin)
2841 # case sensitive match for case insensitive fs
2842 file_exists_exact=file_exists_exact
2843 ;;
2844 *)
2845 file_exists_exact=:
2846 ;;
2847 esac
2848
2849 # Validate options.
2850 #
2851 while getopts :a:d:m:s:x: ch; do
2852 case "${ch}" in
2853 a)
2854 MACHINE_ARCH="${OPTARG}"
2855 ;;
2856 d)
2857 DEST_DIR="${OPTARG}"
2858 ;;
2859 m)
2860 MACHINE="${OPTARG}"
2861 ;;
2862 s)
2863 qarg="$(shell_quote "${OPTARG}")"
2864 N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
2865 SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
2866 if [ -f "${OPTARG}" ]; then
2867 # arg refers to a *.tgz file.
2868 # This may happen twice, for both
2869 # etc.tgz and xetc.tgz, so we build up a
2870 # quoted list in TGZLIST.
2871 TGZMODE=true
2872 TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
2873 # Note that, when TGZMODE is true,
2874 # SRC_ARG is used only for printing
2875 # human-readable messages.
2876 SRC_ARG="${TGZLIST}"
2877 elif [ -d "${OPTARG}" ]; then
2878 # arg refers to a directory.
2879 # It might be a source directory, or a
2880 # directory where the sets have already
2881 # been extracted.
2882 DIRMODE=true
2883 SRC_ARG="${OPTARG}"
2884 if [ -f "${OPTARG}/etc/Makefile" ]; then
2885 SOURCEMODE=true
2886 fi
2887 else
2888 err 2 "Invalid argument for -s option"
2889 fi
2890 ;;
2891 x)
2892 if [ -d "${OPTARG}" ]; then
2893 # arg refers to a directory.
2894 XSRC_DIR="${OPTARG}"
2895 XSRC_DIR_FIX="-x ${OPTARG} "
2896 else
2897 err 2 "Not a directory for -x option"
2898 fi
2899 ;;
2900 "?")
2901 if [ "${OPTARG}" = "?" ]; then
2902 help
2903 return # no further processing or validation
2904 fi
2905 warn "Unknown option -${OPTARG}"
2906 usage
2907 ;;
2908
2909 :)
2910 warn "Missing argument for option -${OPTARG}"
2911 usage
2912 ;;
2913
2914 *)
2915 err 3 "Unimplemented option -${ch}"
2916 ;;
2917 esac
2918 done
2919 shift $((${OPTIND} - 1))
2920 if [ $# -eq 0 ] ; then
2921 warn "Missing operation"
2922 usage
2923 fi
2924 op="$1"
2925 shift
2926
2927 if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
2928 err 2 "Multiple -s args are allowed only with tgz files"
2929 fi
2930 if [ "$N_SRC_ARGS" -eq 0 ]; then
2931 # The default SRC_ARG was set elsewhere
2932 DIRMODE=true
2933 SOURCEMODE=true
2934 SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
2935 fi
2936
2937 # Validate 'diff' first, as it becomes 'check'
2938 #
2939 case "${op}" in
2940
2941 diff)
2942 op=check
2943 DIFF_STYLE=n # default style is RCS
2944 OPTIND=1
2945 while getopts :bcenpuw ch; do
2946 case "${ch}" in
2947 c|e|n|u)
2948 if [ "${DIFF_STYLE}" != "n" -a \
2949 "${DIFF_STYLE}" != "${ch}" ]; then
2950 warn "diff: conflicting output style: -${ch}"
2951 usage
2952 fi
2953 DIFF_STYLE="${ch}"
2954 ;;
2955 b|p|w)
2956 DIFF_OPT="${DIFF_OPT} -${ch}"
2957 ;;
2958 "?")
2959 # NOTE: not supporting diff -?
2960 warn "diff: Unknown option -${OPTARG}"
2961 usage
2962 ;;
2963 :)
2964 warn "diff: Missing argument for option -${OPTARG}"
2965 usage
2966 ;;
2967 *)
2968 err 3 "diff: Unimplemented option -${ch}"
2969 ;;
2970 esac
2971 done
2972 shift $((${OPTIND} - 1))
2973 ;;
2974
2975 esac
2976
2977 # Validate operation and items.
2978 #
2979 case "${op}" in
2980
2981 check|fix)
2982 ITEMS="$*"
2983 : ${ITEMS:="${defaultitems}"}
2984
2985 # ensure that all supplied items are valid
2986 #
2987 for i in ${ITEMS}; do
2988 eval desc=\"\${desc_${i}}\"
2989 [ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
2990 done
2991 ;;
2992
2993 help|usage)
2994 help
2995 return # no further processing or validation
2996 ;;
2997
2998 list)
2999 # processed below
3000 ;;
3001
3002 *)
3003 warn "Unknown operation '"${op}"'"
3004 usage
3005 ;;
3006
3007 esac
3008
3009 #
3010 # If '-s' arg or args specified tgz files, extract them
3011 # to a scratch directory.
3012 #
3013 if $TGZMODE; then
3014 ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
3015 echo "Note: Creating temporary directory ${ETCTGZDIR}"
3016 if ! mkdir "${ETCTGZDIR}"; then
3017 err 2 "Can't create ${ETCTGZDIR}"
3018 fi
3019 ( # subshell to localise changes to "$@"
3020 eval "set -- ${TGZLIST}"
3021 for tgz in "$@"; do
3022 echo "Note: Extracting files from ${tgz}"
3023 cat "${tgz}" | (
3024 cd "${ETCTGZDIR}" &&
3025 tar -zxf -
3026 ) || err 2 "Can't extract ${tgz}"
3027 done
3028 )
3029 SRC_DIR="${ETCTGZDIR}"
3030 else
3031 SRC_DIR="${SRC_ARG}"
3032 fi
3033
3034 [ -d "${SRC_DIR}" ] || err 2 "${SRC_DIR} is not a directory"
3035 [ -d "${DEST_DIR}" ] || err 2 "${DEST_DIR} is not a directory"
3036 [ -n "${MACHINE}" ] || err 2 "\${MACHINE} is not defined"
3037 [ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
3038 if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
3039 err 2 "Files from the etc.tgz set are missing"
3040 fi
3041
3042 # If directories are /, clear them, so various messages
3043 # don't have leading "//". However, this requires
3044 # the use of ${foo:-/} to display the variables.
3045 #
3046 [ "${SRC_DIR}" = "/" ] && SRC_DIR=""
3047 [ "${DEST_DIR}" = "/" ] && DEST_DIR=""
3048
3049 detect_x11
3050
3051 # Perform operation.
3052 #
3053 case "${op}" in
3054
3055 check|fix)
3056 [ -n "${ITEMS}" ] || err 2 "${op}: missing items"
3057
3058 echo "Source directory: ${SRC_DIR:-/}"
3059 if $TGZMODE; then
3060 echo " (extracted from: ${SRC_ARG})"
3061 fi
3062 echo "Target directory: ${DEST_DIR:-/}"
3063 items_passed=
3064 items_failed=
3065 for i in ${ITEMS}; do
3066 echo "${i} ${op}:"
3067 ( eval do_${i} ${op} )
3068 if [ $? -eq 0 ]; then
3069 items_passed="${items_passed} ${i}"
3070 else
3071 items_failed="${items_failed} ${i}"
3072 fi
3073 done
3074
3075 if [ "${op}" = "check" ]; then
3076 plural="checks"
3077 else
3078 plural="fixes"
3079 fi
3080
3081 echo "${PROGNAME} ${plural} passed:${items_passed}"
3082 echo "${PROGNAME} ${plural} failed:${items_failed}"
3083 if [ -n "${items_failed}" ]; then
3084 exitstatus=1;
3085 if [ "${op}" = "check" ]; then
3086 [ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
3087 cat <<_Fix_me_
3088 To fix, run:
3089 ${HOST_SH} ${0} ${SRC_ARGLIST} ${XSRC_DIR_FIX}-d ${DEST_DIR:-/}$m fix${items_failed}
3090 Note that this may overwrite local changes.
3091 _Fix_me_
3092 fi
3093 fi
3094 ;;
3095
3096 list)
3097 echo "Source directory: ${SRC_DIR:-/}"
3098 echo "Target directory: ${DEST_DIR:-/}"
3099 if $TGZMODE; then
3100 echo " (extracted from: ${SRC_ARG})"
3101 fi
3102 list
3103 ;;
3104
3105 *)
3106 # diff, help, usage handled during operation validation
3107 err 3 "Unimplemented operation '"${op}"'"
3108 ;;
3109
3110 esac
3111 }
3112
3113 if [ -n "$POSTINSTALL_FUNCTION" ]; then
3114 eval "$POSTINSTALL_FUNCTION"
3115 exit 0
3116 fi
3117
3118 # defaults
3119 #
3120 PROGNAME="${0##*/}"
3121 SRC_ARG="/usr/src"
3122 DEST_DIR="/"
3123 : ${MACHINE:="$( uname -m )"} # assume native build if $MACHINE is not set
3124 : ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
3125
3126 DIFF_STYLE=
3127 DIFF_OPT=
3128 NOT_FIXED=" (FIX MANUALLY)"
3129 SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
3130 trap "${RM} -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15 # HUP INT QUIT TERM
3131
3132 umask 022
3133 exec 3>/dev/null
3134 exec 4>/dev/null
3135 exitstatus=0
3136
3137 main "$@"
3138 ${RM} -rf "${SCRATCHDIR}"
3139 exit $exitstatus
3140