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