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