]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsdconfig/share/sysrc.subr
MFC r307402: Guard against bad service name argument(s) to load_rc_config()
[FreeBSD/stable/10.git] / usr.sbin / bsdconfig / share / sysrc.subr
1 if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
2 #
3 # Copyright (c) 2006-2015 Devin Teske
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 # SUCH DAMAGE.
26 #
27 # $FreeBSD$
28 #
29 ############################################################ INCLUDES
30
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 [ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
33
34 BSDCFG_LIBE="/usr/libexec/bsdconfig"
35 if [ ! "$_SYSRC_JAILED" ]; then
36         f_dprintf "%s: loading includes..." sysrc.subr
37         f_include_lang $BSDCFG_LIBE/include/messages.subr
38 fi
39
40 ############################################################ CONFIGURATION
41
42 #
43 # Standard pathnames (inherit values from shell if available)
44 #
45 : ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
46
47 ############################################################ GLOBALS
48
49 #
50 # Global exit status variables
51 #
52 SUCCESS=0
53 FAILURE=1
54
55 #
56 # Valid characters that can appear in an sh(1) variable name
57 #
58 # Please note that the character ranges A-Z and a-z should be avoided because
59 # these can include accent characters (which are not valid in a variable name).
60 # For example, A-Z matches any character that sorts after A but before Z,
61 # including A and Z. Although ASCII order would make more sense, that is not
62 # how it works.
63 #
64 VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
65
66 ############################################################ FUNCTIONS
67
68 # f_clean_env [ --except $varname ... ]
69 #
70 # Unset all environment variables in the current scope. An optional list of
71 # arguments can be passed, indicating which variables to avoid unsetting; the
72 # `--except' is required to enable the exclusion-list as the remainder of
73 # positional arguments.
74 #
75 # Be careful not to call this in a shell that you still expect to perform
76 # $PATH expansion in, because this will blow $PATH away. This is best used
77 # within a sub-shell block "(...)" or "$(...)" or "`...`".
78 #
79 f_clean_env()
80 {
81         local var arg except=
82
83         #
84         # Should we process an exclusion-list?
85         #
86         if [ "$1" = "--except" ]; then
87                 except=1
88                 shift 1
89         fi
90
91         #
92         # Loop over a list of variable names from set(1) built-in.
93         #
94         for var in $( set | awk -F= \
95                 '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
96                 | grep -v '^except$'
97         ); do
98                 #
99                 # In POSIX bourne-shell, attempting to unset(1) OPTIND results
100                 # in "unset: Illegal number:" and causes abrupt termination.
101                 #
102                 [ "$var" = OPTIND ] && continue
103
104                 #
105                 # Process the exclusion-list?
106                 #
107                 if [ "$except" ]; then
108                         for arg in "$@" ""; do
109                                 [ "$var" = "$arg" ] && break
110                         done
111                         [ "$arg" ] && continue
112                 fi
113
114                 unset "$var"
115         done
116 }
117
118 # f_sysrc_get $varname
119 #
120 # Get a system configuration setting from the collection of system-
121 # configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and
122 # /etc/rc.conf.local)
123 #
124 # NOTE: Additional shell parameter-expansion formats are supported. For
125 # example, passing an argument of "hostname%%.*" (properly quoted) will
126 # return the hostname up to (but not including) the first `.' (see sh(1),
127 # "Parameter Expansion" for more information on additional formats).
128 #
129 f_sysrc_get()
130 {
131         # Sanity check
132         [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
133
134         # Taint-check variable name
135         case "$1" in
136         [0-9]*)
137                 # Don't expand possible positional parameters
138                 return $FAILURE ;;
139         *)
140                 [ "$1" ] || return $FAILURE
141         esac
142
143         ( # Execute within sub-shell to protect parent environment
144
145                 #
146                 # Clear the environment of all variables, preventing the
147                 # expansion of normals such as `PS1', `TERM', etc.
148                 #
149                 f_clean_env --except IFS RC_CONFS RC_DEFAULTS
150
151                 . "$RC_DEFAULTS" > /dev/null 2>&1
152
153                 unset RC_DEFAULTS
154                         # no longer needed
155
156                 #
157                 # If the query is for `rc_conf_files' then store the value that
158                 # we inherited from sourcing RC_DEFAULTS (above) so that we may
159                 # conditionally restore this value after source_rc_confs in the
160                 # event that RC_CONFS does not customize the value.
161                 #
162                 if [ "$1" = "rc_conf_files" ]; then
163                         _rc_conf_files="$rc_conf_files"
164                 fi
165
166                 #
167                 # If RC_CONFS is defined, set $rc_conf_files to an explicit
168                 # value, modifying the default behavior of source_rc_confs().
169                 #
170                 if [ "${RC_CONFS+set}" ]; then
171                         rc_conf_files="$RC_CONFS"
172                         _rc_confs_set=1
173                 fi
174
175                 source_rc_confs > /dev/null 2>&1
176
177                 #
178                 # If the query was for `rc_conf_files' AND after calling
179                 # source_rc_confs the value has not changed, then we should
180                 # restore the value to the one inherited from RC_DEFAULTS
181                 # before performing the final query (preventing us from
182                 # returning what was set via RC_CONFS when the intent was
183                 # instead to query the value from the file(s) specified).
184                 #
185                 if [ "$1" = "rc_conf_files" -a \
186                      "$_rc_confs_set" -a \
187                      "$rc_conf_files" = "$RC_CONFS" \
188                 ]; then
189                         rc_conf_files="$_rc_conf_files"
190                         unset _rc_conf_files
191                         unset _rc_confs_set
192                 fi
193
194                 unset RC_CONFS
195                         # no longer needed
196
197                 #
198                 # This must be the last functional line for both the sub-shell
199                 # and the function to preserve the return status from formats
200                 # such as "${varname?}" and "${varname:?}" (see "Parameter
201                 # Expansion" in sh(1) for more information).
202                 #
203                 eval echo '"${'"$1"'}"' 2> /dev/null
204         )
205 }
206
207 # f_sysrc_service_configs [-a|-p] $name [$var_to_set]
208 #
209 # Get a list of optional `rc.conf.d' entries sourced by system `rc.d' script
210 # $name (see rc.subr(8) for additional information on `rc.conf.d'). If $name
211 # exists in `/etc/rc.d' or $local_startup directories and is an rc(8) script
212 # the result is a space separated list of `rc.conf.d' entries sourced by the
213 # $name `rc.d' script. Otherwise, if $name exists as a binary `rc.d' script,
214 # the result is ``/etc/rc.conf.d/$name /usr/local/etc/rc.conf.d/$name''. The
215 # result is NULL if $name does not exist.
216 #
217 # If $var_to_set is missing or NULL, output is to standard out. Returns success
218 # if $name was found, failure otherwise.
219 #
220 # If `-a' flag is given and $var_to_set is non-NULL, append result to value of
221 # $var_to_set rather than overwriting current contents.
222 #
223 # If `-p' flag is given and $var_to_set is non-NULL, prepend result to value of
224 # $var_to_set rather than overwriting current contents.
225 #
226 # NB: The `-a' and `-p' option flags are mutually exclusive.
227 #
228 f_sysrc_service_configs()
229 {
230         local OPTIND=1 OPTARG __flag __append= __prepend=
231         local __local_startup __dir __spath __stype __names=
232
233         while getopts ap __flag; do
234                 case "$__flag" in
235                 a) __append=1 __prepend= ;;
236                 p) __prepend=1 __append= ;;
237                 esac
238         done
239         shift $(( $OPTIND - 1 ))
240
241         [ $# -gt 0 ] || return $FAILURE
242         local __sname="$1" __var_to_set="$2"
243
244         __local_startup=$( f_sysrc_get local_startup )
245         for __dir in /etc/rc.d $__local_startup; do
246                 __spath="$__dir/$__sname"
247                 [ -f "$__spath" -a -x "$__spath" ] || __spath= continue
248                 break
249         done
250         [ "$__spath" ] || return $FAILURE
251
252         __stype=$( file -b "$__spath" 2> /dev/null )
253         case "$__stype" in
254         *"shell script"*)
255                 __names=$( exec 9<&1 1>&- 2>&-
256                         last_name=
257                         print_name() {
258                                 local name="$1"
259                                 case "$name" in
260                                 ""|.|..|*/*|"$last_name") return ;;
261                                 esac
262                                 echo "$name" >&9
263                                 last_name="$name"
264                         }
265                         eval "$( awk '{
266                                 gsub(/load_rc_config /, "print_name ")
267                                 gsub(/run_rc_command /, ": ")
268                                 print
269                         }' "$__spath" )"
270                 ) ;;
271         *)
272                 __names="$__sname"
273         esac
274
275         local __name __test_path __configs=
276         for __name in $__names; do
277                 for __dir in /etc/rc.d $__local_startup; do
278                         __test_path="${__dir%/rc.d}/rc.conf.d/$__name"
279                         [ -d "$__test_path" ] ||
280                                 __configs="$__configs $__test_path" continue
281                         for __test_path in "$__test_path"/*; do
282                                 [ -f "$__test_path" ] || continue
283                                 __configs="$__configs $__test_path"
284                         done    
285                 done
286         done
287         __configs="${__configs# }"
288
289         if [ "$__var_to_set" ]; then
290                 local __cur=
291                 [ "$__append" -o "$__prepend" ] &&
292                         f_getvar "$__var_to_set" __cur
293                 [ "$__append"  ] && __configs="$__cur{$__cur:+ }$__configs"
294                 [ "$__prepend" ] && __configs="$__configs${__cur:+ }$__cur"
295                 setvar "$__var_to_set" "$__configs"
296         else
297                 echo "$__configs"
298         fi
299
300         return $SUCCESS
301 }
302
303 # f_sysrc_get_default $varname
304 #
305 # Get a system configuration default setting from the default rc.conf(5) file
306 # (or whatever RC_DEFAULTS points at).
307 #
308 f_sysrc_get_default()
309 {
310         # Sanity check
311         [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
312
313         # Taint-check variable name
314         case "$1" in
315         [0-9]*)
316                 # Don't expand possible positional parameters
317                 return $FAILURE ;;
318         *)
319                 [ "$1" ] || return $FAILURE
320         esac
321
322         ( # Execute within sub-shell to protect parent environment
323
324                 #
325                 # Clear the environment of all variables, preventing the
326                 # expansion of normals such as `PS1', `TERM', etc.
327                 #
328                 f_clean_env --except RC_DEFAULTS
329
330                 . "$RC_DEFAULTS" > /dev/null 2>&1
331
332                 unset RC_DEFAULTS
333                         # no longer needed
334
335                 #
336                 # This must be the last functional line for both the sub-shell
337                 # and the function to preserve the return status from formats
338                 # such as "${varname?}" and "${varname:?}" (see "Parameter
339                 # Expansion" in sh(1) for more information).
340                 #
341                 eval echo '"${'"$1"'}"' 2> /dev/null
342         )
343 }
344
345 # f_sysrc_find $varname
346 #
347 # Find which file holds the effective last-assignment to a given variable
348 # within the rc.conf(5) file(s).
349 #
350 # If the variable is found in any of the rc.conf(5) files, the function prints
351 # the filename it was found in and then returns success. Otherwise output is
352 # NULL and the function returns with error status.
353 #
354 f_sysrc_find()
355 {
356         local varname="${1%%[!$VALID_VARNAME_CHARS]*}"
357         local regex="^[[:space:]]*$varname="
358         local rc_conf_files="$( f_sysrc_get rc_conf_files )"
359         local conf_files=
360         local file
361
362         # Check parameters
363         case "$varname" in
364         ""|[0-9]*) return $FAILURE
365         esac
366
367         #
368         # If RC_CONFS is defined, set $rc_conf_files to an explicit
369         # value, modifying the default behavior of source_rc_confs().
370         #
371         [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS"
372
373         #
374         # Reverse the order of files in rc_conf_files (the boot process sources
375         # these in order, so we will search them in reverse-order to find the
376         # last-assignment -- the one that ultimately effects the environment).
377         #
378         for file in $rc_conf_files; do
379                 conf_files="$file${conf_files:+ }$conf_files"
380         done
381
382         #
383         # Append the defaults file (since directives in the defaults file
384         # indeed affect the boot process, we'll want to know when a directive
385         # is found there).
386         #
387         conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
388
389         #
390         # Find which file matches assignment to the given variable name.
391         #
392         for file in $conf_files; do
393                 [ -f "$file" -a -r "$file" ] || continue
394                 if grep -Eq "$regex" $file; then
395                         echo $file
396                         return $SUCCESS
397                 fi
398         done
399
400         return $FAILURE # Not found
401 }
402
403 # f_sysrc_desc $varname
404 #
405 # Attempts to return the comments associated with varname from the rc.conf(5)
406 # defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
407 #
408 # Multi-line comments are joined together. Results are NULL if no description
409 # could be found.
410 #
411 # This function is a two-parter. Below is the awk(1) portion of the function,
412 # afterward is the sh(1) function which utilizes the below awk script.
413 #
414 f_sysrc_desc_awk='
415 # Variables that should be defined on the invocation line:
416 #       -v varname="varname"
417 #
418 BEGIN {
419         regex = "^[[:space:]]*"varname"="
420         found = 0
421         buffer = ""
422 }
423 {
424         if ( ! found )
425         {
426                 if ( ! match($0, regex) ) next
427
428                 found = 1
429                 sub(/^[^#]*(#[[:space:]]*)?/, "")
430                 buffer = $0
431                 next
432         }
433
434         if ( !/^[[:space:]]*#/ ||
435               /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
436               /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
437               /^[[:space:]]*$/ ) exit
438
439         sub(/(.*#)*[[:space:]]*/, "")
440         buffer = buffer" "$0
441 }
442 END {
443         # Clean up the buffer
444         sub(/^[[:space:]]*/, "", buffer)
445         sub(/[[:space:]]*$/, "", buffer)
446
447         print buffer
448         exit ! found
449 }
450 '
451 f_sysrc_desc()
452 {
453         awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
454 }
455
456 # f_sysrc_set $varname $new_value
457 #
458 # Change a setting in the system configuration files (edits the files in-place
459 # to change the value in the last assignment to the variable). If the variable
460 # does not appear in the source file, it is appended to the end of the primary
461 # system configuration file `/etc/rc.conf'.
462 #
463 # This function is a two-parter. Below is the awk(1) portion of the function,
464 # afterward is the sh(1) function which utilizes the below awk script.
465 #
466 f_sysrc_set_awk='
467 # Variables that should be defined on the invocation line:
468 #       -v varname="varname"
469 #       -v new_value="new_value"
470 #
471 BEGIN {
472         regex = "^[[:space:]]*"varname"="
473         found = retval = 0
474 }
475 {
476         # If already found... just spew
477         if ( found ) { print; next }
478
479         # Does this line match an assignment to our variable?
480         if ( ! match($0, regex) ) { print; next }
481
482         # Save important match information
483         found = 1
484         matchlen = RSTART + RLENGTH - 1
485
486         # Store the value text for later munging
487         value = substr($0, matchlen + 1, length($0) - matchlen)
488
489         # Store the first character of the value
490         t1 = t2 = substr(value, 0, 1)
491
492         # Assignment w/ back-ticks, expression, or misc.
493         # We ignore these since we did not generate them
494         #
495         if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
496
497         # Assignment w/ single-quoted value
498         else if ( t1 == "'\''" ) {
499                 sub(/^'\''[^'\'']*/, "", value)
500                 if ( length(value) == 0 ) t2 = ""
501                 sub(/^'\''/, "", value)
502         }
503
504         # Assignment w/ double-quoted value
505         else if ( t1 == "\"" ) {
506                 sub(/^"(.*\\\\+")*[^"]*/, "", value)
507                 if ( length(value) == 0 ) t2 = ""
508                 sub(/^"/, "", value)
509         }
510
511         # Assignment w/ non-quoted value
512         else if ( t1 ~ /[^[:space:];]/ ) {
513                 t1 = t2 = "\""
514                 sub(/^[^[:space:]]*/, "", value)
515         }
516
517         # Null-assignment
518         else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
519
520         printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
521                 t1, new_value, t2, value
522 }
523 END { exit retval }
524 '
525 f_sysrc_set()
526 {
527         local funcname=f_sysrc_set
528         local varname="$1" new_value="$2"
529
530         # Check arguments
531         [ "$varname" ] || return $FAILURE
532
533         #
534         # Find which rc.conf(5) file contains the last-assignment
535         #
536         local not_found=
537         local file="$( f_sysrc_find "$varname" )"
538         if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
539                 #
540                 # We either got a null response (not found) or the variable
541                 # was only found in the rc.conf(5) defaults. In either case,
542                 # let's instead modify the first file from $rc_conf_files.
543                 #
544
545                 not_found=1
546
547                 #
548                 # If RC_CONFS is defined, use $RC_CONFS
549                 # rather than $rc_conf_files.
550                 #
551                 if [ "${RC_CONFS+set}" ]; then
552                         file="${RC_CONFS%%[$IFS]*}"
553                 else
554                         file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
555                 fi
556         fi
557
558         #
559         # If not found, append new value to last file and return.
560         #
561         if [ "$not_found" ]; then
562                 echo "$varname=\"$new_value\"" >> "$file"
563                 return $?
564         fi
565
566         #
567         # Perform sanity checks.
568         #
569         if [ ! -w "$file" ]; then
570                 f_err "$msg_cannot_create_permission_denied\n" \
571                       "$pgm" "$file"
572                 return $FAILURE
573         fi
574
575         #
576         # Create a new temporary file to write to.
577         #
578         local tmpfile
579         if ! f_eval_catch -dk tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm"
580         then
581                 echo "$tmpfile" >&2
582                 return $FAILURE
583         fi
584
585         #
586         # Fixup permissions (else we're in for a surprise, as mktemp(1) creates
587         # the temporary file with 0600 permissions, and if we simply mv(1) the
588         # temporary file over the destination, the destination will inherit the
589         # permissions from the temporary file).
590         #
591         local mode
592         f_eval_catch -dk mode $funcname stat 'stat -f "%%#Lp" "%s"' "$file" ||
593                 mode=0644
594         f_eval_catch -d $funcname chmod 'chmod "%s" "%s"' "$mode" "$tmpfile"
595
596         #
597         # Fixup ownership. The destination file _is_ writable (we tested
598         # earlier above). However, this will fail if we don't have sufficient
599         # permissions (so we throw stderr into the bit-bucket).
600         #
601         local owner
602         f_eval_catch -dk owner $funcname stat \
603                 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
604         f_eval_catch -d $funcname chown 'chown "%s" "%s"' "$owner" "$tmpfile"
605
606         #
607         # Operate on the matching file, replacing only the last occurrence.
608         #
609         local new_contents retval
610         new_contents=$( tail -r $file 2> /dev/null )
611         new_contents=$( echo "$new_contents" | awk -v varname="$varname" \
612                 -v new_value="$new_value" "$f_sysrc_set_awk" )
613         retval=$?
614
615         #
616         # Write the temporary file contents.
617         #
618         echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE
619         if [ $retval -ne $SUCCESS ]; then
620                 echo "$varname=\"$new_value\"" >> "$tmpfile"
621         fi
622
623         #
624         # Taint-check our results.
625         #
626         if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"; then
627                 f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
628                 rm -f "$tmpfile"
629                 return $FAILURE
630         fi
631
632         #
633         # Finally, move the temporary file into place.
634         #
635         f_eval_catch -de $funcname mv 'mv "%s" "%s"' "$tmpfile" "$file"
636 }
637
638 # f_sysrc_delete $varname
639 #
640 # Remove a setting from the system configuration files (edits files in-place).
641 # Deletes all assignments to the given variable in all config files. If the
642 # `-f file' option is passed, the removal is restricted to only those files
643 # specified, otherwise the system collection of rc_conf_files is used.
644 #
645 # This function is a two-parter. Below is the awk(1) portion of the function,
646 # afterward is the sh(1) function which utilizes the below awk script.
647 #
648 f_sysrc_delete_awk='
649 # Variables that should be defined on the invocation line:
650 #       -v varname="varname"
651 #
652 BEGIN {
653         regex = "^[[:space:]]*"varname"="
654         found = 0
655 }
656 {
657         if ( $0 ~ regex )
658                 found = 1
659         else
660                 print
661 }
662 END { exit ! found }
663 '
664 f_sysrc_delete()
665 {
666         local funcname=f_sysrc_delete
667         local varname="$1"
668         local file
669
670         # Check arguments
671         [ "$varname" ] || return $FAILURE
672
673         #
674         # Operate on each of the specified files
675         #
676         local tmpfile
677         for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do
678                 [ -e "$file" ] || continue
679
680                 #
681                 # Create a new temporary file to write to.
682                 #
683                 if ! f_eval_catch -dk tmpfile $funcname mktemp \
684                         'mktemp -t "%s"' "$pgm"
685                 then
686                         echo "$tmpfile" >&2
687                         return $FAILURE
688                 fi
689
690                 #
691                 # Fixup permissions and ownership (mktemp(1) defaults to 0600
692                 # permissions) to instead match the destination file.
693                 #
694                 local mode owner
695                 f_eval_catch -dk mode $funcname stat \
696                         'stat -f "%%#Lp" "%s"' "$file" || mode=0644
697                 f_eval_catch -dk owner $funcname stat \
698                         'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
699                 f_eval_catch -d $funcname chmod \
700                         'chmod "%s" "%s"' "$mode" "$tmpfile"
701                 f_eval_catch -d $funcname chown \
702                         'chown "%s" "%s"' "$owner" "$tmpfile"
703
704                 #
705                 # Operate on the file, removing all occurrences, saving the
706                 # output in our temporary file.
707                 #
708                 awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
709                         > "$tmpfile"
710                 if [ $? -ne $SUCCESS ]; then
711                         # The file didn't contain any assignments
712                         rm -f "$tmpfile"
713                         continue
714                 fi
715
716                 #
717                 # Taint-check our results.
718                 #
719                 if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"
720                 then
721                         f_err "$msg_previous_syntax_errors\n" \
722                               "$pgm" "$file"
723                         rm -f "$tmpfile"
724                         return $FAILURE
725                 fi
726
727                 #
728                 # Perform sanity checks
729                 #
730                 if [ ! -w "$file" ]; then
731                         f_err "$msg_permission_denied\n" "$pgm" "$file"
732                         rm -f "$tmpfile"
733                         return $FAILURE
734                 fi
735
736                 #
737                 # Finally, move the temporary file into place.
738                 #
739                 f_eval_catch -de $funcname mv \
740                         'mv "%s" "%s"' "$tmpfile" "$file" || return $FAILURE
741         done
742 }
743
744 ############################################################ MAIN
745
746 f_dprintf "%s: Successfully loaded." sysrc.subr
747
748 fi # ! $_SYSRC_SUBR