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