sysrc.subr revision 240798
1214501Srpauloif [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
2214501Srpaulo#
3214501Srpaulo# Copyright (c) 2006-2012 Devin Teske
4214501Srpaulo# All Rights Reserved.
5252726Srpaulo#
6252726Srpaulo# Redistribution and use in source and binary forms, with or without
7214501Srpaulo# modification, are permitted provided that the following conditions
8214501Srpaulo# are met:
9214501Srpaulo# 1. Redistributions of source code must retain the above copyright
10214501Srpaulo#    notice, this list of conditions and the following disclaimer.
11214501Srpaulo# 2. Redistributions in binary form must reproduce the above copyright
12214501Srpaulo#    notice, this list of conditions and the following disclaimer in the
13214501Srpaulo#    documentation and/or other materials provided with the distribution.
14214501Srpaulo#
15214501Srpaulo# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16214501Srpaulo# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17214501Srpaulo# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18214501Srpaulo# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19214501Srpaulo# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20214501Srpaulo# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21214501Srpaulo# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22252726Srpaulo# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23252726Srpaulo# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24252726Srpaulo# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25252726Srpaulo# SUCH DAMAGE.
26252726Srpaulo#
27214501Srpaulo# $FreeBSD: head/usr.sbin/bsdconfig/share/sysrc.subr 240798 2012-09-22 04:04:02Z dteske $
28214501Srpaulo#
29214501Srpaulo############################################################ INCLUDES
30214501Srpaulo
31214501SrpauloBSDCFG_SHARE="/usr/share/bsdconfig"
32252726Srpaulo. $BSDCFG_SHARE/common.subr || exit 1
33214501Srpaulo
34214501SrpauloBSDCFG_LIBE="/usr/libexec/bsdconfig"
35214501Srpaulof_include_lang $BSDCFG_LIBE/include/messages.subr
36214501Srpaulo
37214501Srpaulo############################################################ CONFIGURATION
38214501Srpaulo
39214501Srpaulo#
40252726Srpaulo# Standard pathnames (inherit values from shell if available)
41252726Srpaulo#
42214501Srpaulo: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
43214501Srpaulo
44214501Srpaulo############################################################ GLOBALS
45214501Srpaulo
46252726Srpaulo#
47214501Srpaulo# Global exit status variables
48214501Srpaulo#
49214501SrpauloSUCCESS=0
50214501SrpauloFAILURE=1
51214501Srpaulo
52252726Srpaulo############################################################ FUNCTIONS
53214501Srpaulo
54214501Srpaulo# f_clean_env [ --except $varname ... ]
55214501Srpaulo#
56214501Srpaulo# Unset all environment variables in the current scope. An optional list of
57214501Srpaulo# arguments can be passed, indicating which variables to avoid unsetting; the
58252726Srpaulo# `--except' is required to enable the exclusion-list as the remainder of
59214501Srpaulo# positional arguments.
60214501Srpaulo#
61214501Srpaulo# Be careful not to call this in a shell that you still expect to perform
62214501Srpaulo# $PATH expansion in, because this will blow $PATH away. This is best used
63214501Srpaulo# within a sub-shell block "(...)" or "$(...)" or "`...`".
64214501Srpaulo#
65214501Srpaulof_clean_env()
66214501Srpaulo{
67214501Srpaulo	local var arg except=
68214501Srpaulo
69214501Srpaulo	#
70214501Srpaulo	# Should we process an exclusion-list?
71214501Srpaulo	#
72214501Srpaulo	if [ "$1" = "--except" ]; then
73214501Srpaulo		except=1
74214501Srpaulo		shift 1
75214501Srpaulo	fi
76214501Srpaulo
77214501Srpaulo	#
78214501Srpaulo	# Loop over a list of variable names from set(1) built-in.
79214501Srpaulo	#
80214501Srpaulo	for var in $( set | awk -F= \
81214501Srpaulo		'/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
82214501Srpaulo		| grep -v '^except$'
83214501Srpaulo	); do
84214501Srpaulo		#
85214501Srpaulo		# In POSIX bourne-shell, attempting to unset(1) OPTIND results
86214501Srpaulo		# in "unset: Illegal number:" and causes abrupt termination.
87214501Srpaulo		#
88214501Srpaulo		[ "$var" = OPTIND ] && continue
89214501Srpaulo
90214501Srpaulo		#
91214501Srpaulo		# Process the exclusion-list?
92214501Srpaulo		#
93214501Srpaulo		if [ "$except" ]; then
94214501Srpaulo			for arg in "$@" ""; do
95252726Srpaulo				[ "$var" = "$arg" ] && break
96214501Srpaulo			done
97214501Srpaulo			[ "$arg" ] && continue
98252726Srpaulo		fi
99214501Srpaulo
100214501Srpaulo		unset "$var"
101214501Srpaulo	done
102214501Srpaulo}
103214501Srpaulo
104214501Srpaulo# f_sysrc_get $varname
105252726Srpaulo#
106252726Srpaulo# Get a system configuration setting from the collection of system-
107214501Srpaulo# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf
108214501Srpaulo# and /etc/rc.conf).
109214501Srpaulo#
110214501Srpaulo# NOTE: Additional shell parameter-expansion formats are supported. For
111252726Srpaulo# example, passing an argument of "hostname%%.*" (properly quoted) will
112214501Srpaulo# return the hostname up to (but not including) the first `.' (see sh(1),
113214501Srpaulo# "Parameter Expansion" for more information on additional formats).
114214501Srpaulo#
115214501Srpaulof_sysrc_get()
116214501Srpaulo{
117214501Srpaulo	# Sanity check
118252726Srpaulo	[ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
119252726Srpaulo
120252726Srpaulo	# Taint-check variable name
121214501Srpaulo	case "$1" in
122214501Srpaulo	[0-9]*)
123214501Srpaulo		# Don't expand possible positional parameters
124252726Srpaulo		return $FAILURE;;
125252726Srpaulo	*)
126252726Srpaulo		[ "$1" ] || return $FAILURE
127252726Srpaulo	esac
128252726Srpaulo
129252726Srpaulo	( # Execute within sub-shell to protect parent environment
130252726Srpaulo
131252726Srpaulo		#
132252726Srpaulo		# Clear the environment of all variables, preventing the
133214501Srpaulo		# expansion of normals such as `PS1', `TERM', etc.
134214501Srpaulo		#
135214501Srpaulo		f_clean_env --except RC_CONFS RC_DEFAULTS
136252726Srpaulo
137214501Srpaulo		. "$RC_DEFAULTS" > /dev/null 2>&1
138214501Srpaulo
139214501Srpaulo		unset RC_DEFAULTS
140214501Srpaulo			# no longer needed
141214501Srpaulo
142214501Srpaulo		#
143214501Srpaulo		# If the query is for `rc_conf_files' then store the value that
144214501Srpaulo		# we inherited from sourcing RC_DEFAULTS (above) so that we may
145214501Srpaulo		# conditionally restore this value after source_rc_confs in the
146214501Srpaulo		# event that RC_CONFS does not customize the value.
147214501Srpaulo		#
148214501Srpaulo		if [ "$1" = "rc_conf_files" ]; then
149252726Srpaulo			_rc_conf_files="$rc_conf_files"
150252726Srpaulo		fi
151252726Srpaulo
152252726Srpaulo		#
153252726Srpaulo		# If RC_CONFS is defined, set $rc_conf_files to an explicit
154252726Srpaulo		# value, modifying the default behavior of source_rc_confs().
155252726Srpaulo		#
156252726Srpaulo		if [ "${RC_CONFS+set}" ]; then
157214501Srpaulo			rc_conf_files="$RC_CONFS"
158214501Srpaulo			_rc_confs_set=1
159214501Srpaulo		fi
160214501Srpaulo
161214501Srpaulo		source_rc_confs > /dev/null 2>&1
162214501Srpaulo
163214501Srpaulo		#
164214501Srpaulo		# If the query was for `rc_conf_files' AND after calling
165214501Srpaulo		# source_rc_confs the value has not changed, then we should
166214501Srpaulo		# restore the value to the one inherited from RC_DEFAULTS
167214501Srpaulo		# before performing the final query (preventing us from
168214501Srpaulo		# returning what was set via RC_CONFS when the intent was
169214501Srpaulo		# instead to query the value from the file(s) specified).
170214501Srpaulo		#
171214501Srpaulo		if [ "$1" = "rc_conf_files" -a \
172214501Srpaulo		     "$_rc_confs_set" -a \
173214501Srpaulo		     "$rc_conf_files" = "$RC_CONFS" \
174214501Srpaulo		]; then
175214501Srpaulo			rc_conf_files="$_rc_conf_files"
176214501Srpaulo			unset _rc_conf_files
177214501Srpaulo			unset _rc_confs_set
178214501Srpaulo		fi
179214501Srpaulo
180214501Srpaulo		unset RC_CONFS
181214501Srpaulo			# no longer needed
182214501Srpaulo
183214501Srpaulo		#
184214501Srpaulo		# This must be the last functional line for both the sub-shell
185214501Srpaulo		# and the function to preserve the return status from formats
186214501Srpaulo		# such as "${varname?}" and "${varname:?}" (see "Parameter
187214501Srpaulo		# Expansion" in sh(1) for more information).
188214501Srpaulo		#
189214501Srpaulo		eval echo '"${'"$1"'}"' 2> /dev/null
190214501Srpaulo	)
191252726Srpaulo}
192252726Srpaulo
193252726Srpaulo# f_sysrc_get_default $varname
194252726Srpaulo#
195252726Srpaulo# Get a system configuration default setting from the default rc.conf(5) file
196252726Srpaulo# (or whatever RC_DEFAULTS points at).
197252726Srpaulo#
198252726Srpaulof_sysrc_get_default()
199252726Srpaulo{
200252726Srpaulo	# Sanity check
201252726Srpaulo	[ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
202252726Srpaulo
203214501Srpaulo	# Taint-check variable name
204252726Srpaulo	case "$1" in
205252726Srpaulo	[0-9]*)
206252726Srpaulo		# Don't expand possible positional parameters
207252726Srpaulo		return $FAILURE;;
208252726Srpaulo	*)
209252726Srpaulo		[ "$1" ] || return $FAILURE
210252726Srpaulo	esac
211252726Srpaulo
212214501Srpaulo	( # Execute within sub-shell to protect parent environment
213214501Srpaulo
214214501Srpaulo		#
215214501Srpaulo		# Clear the environment of all variables, preventing the
216214501Srpaulo		# expansion of normals such as `PS1', `TERM', etc.
217214501Srpaulo		#
218214501Srpaulo		f_clean_env --except RC_DEFAULTS
219214501Srpaulo
220214501Srpaulo		. "$RC_DEFAULTS" > /dev/null 2>&1
221214501Srpaulo
222214501Srpaulo		unset RC_DEFAULTS
223214501Srpaulo			# no longer needed
224214501Srpaulo
225214501Srpaulo		#
226214501Srpaulo		# This must be the last functional line for both the sub-shell
227214501Srpaulo		# and the function to preserve the return status from formats
228214501Srpaulo		# such as "${varname?}" and "${varname:?}" (see "Parameter
229214501Srpaulo		# Expansion" in sh(1) for more information).
230214501Srpaulo		#
231214501Srpaulo		eval echo '"${'"$1"'}"' 2> /dev/null
232214501Srpaulo	)
233214501Srpaulo}
234214501Srpaulo
235214501Srpaulo# f_sysrc_find $varname
236252726Srpaulo#
237252726Srpaulo# Find which file holds the effective last-assignment to a given variable
238252726Srpaulo# within the rc.conf(5) file(s).
239252726Srpaulo#
240214501Srpaulo# If the variable is found in any of the rc.conf(5) files, the function prints
241214501Srpaulo# the filename it was found in and then returns success. Otherwise output is
242214501Srpaulo# NULL and the function returns with error status.
243214501Srpaulo#
244214501Srpaulof_sysrc_find()
245214501Srpaulo{
246214501Srpaulo	local varname="$1"
247214501Srpaulo	local regex="^[[:space:]]*$varname="
248214501Srpaulo	local rc_conf_files="$( f_sysrc_get rc_conf_files )"
249214501Srpaulo	local conf_files=
250214501Srpaulo	local file
251214501Srpaulo
252214501Srpaulo	# Check parameters
253214501Srpaulo	[ "$varname" ] || return $FAILURE
254214501Srpaulo
255214501Srpaulo	#
256214501Srpaulo	# If RC_CONFS is defined, set $rc_conf_files to an explicit
257214501Srpaulo	# value, modifying the default behavior of source_rc_confs().
258214501Srpaulo	#
259214501Srpaulo	[ "$RC_CONFS" ] && rc_conf_files="$RC_CONFS"
260214501Srpaulo
261214501Srpaulo	#
262214501Srpaulo	# Reverse the order of files in rc_conf_files (the boot process sources
263214501Srpaulo	# these in order, so we will search them in reverse-order to find the
264214501Srpaulo	# last-assignment -- the one that ultimately effects the environment).
265214501Srpaulo	#
266214501Srpaulo	for file in $rc_conf_files; do
267214501Srpaulo		conf_files="$file${conf_files:+ }$conf_files"
268214501Srpaulo	done
269252726Srpaulo
270252726Srpaulo	#
271252726Srpaulo	# Append the defaults file (since directives in the defaults file
272252726Srpaulo	# indeed affect the boot process, we'll want to know when a directive
273252726Srpaulo	# is found there).
274252726Srpaulo	#
275252726Srpaulo	conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
276252726Srpaulo
277252726Srpaulo	#
278252726Srpaulo	# Find which file matches assignment to the given variable name.
279252726Srpaulo	#
280252726Srpaulo	for file in $conf_files; do
281252726Srpaulo		[ -f "$file" -a -r "$file" ] || continue
282252726Srpaulo		if grep -Eq "$regex" $file; then
283252726Srpaulo			echo $file
284252726Srpaulo			return $SUCCESS
285252726Srpaulo		fi
286252726Srpaulo	done
287252726Srpaulo
288252726Srpaulo	return $FAILURE # Not found
289252726Srpaulo}
290252726Srpaulo
291252726Srpaulo# f_sysrc_desc $varname
292214501Srpaulo#
293214501Srpaulo# Attempts to return the comments associated with varname from the rc.conf(5)
294214501Srpaulo# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
295214501Srpaulo#
296214501Srpaulo# Multi-line comments are joined together. Results are NULL if no description
297214501Srpaulo# could be found.
298214501Srpaulo#
299214501Srpaulo# This function is a two-parter. Below is the awk(1) portion of the function,
300214501Srpaulo# afterward is the sh(1) function which utilizes the below awk script.
301214501Srpaulo#
302214501Srpaulof_sysrc_desc_awk='
303214501Srpaulo# Variables that should be defined on the invocation line:
304214501Srpaulo# 	-v varname="varname"
305214501Srpaulo#
306214501SrpauloBEGIN {
307214501Srpaulo	regex = "^[[:space:]]*"varname"="
308214501Srpaulo	found = 0
309214501Srpaulo	buffer = ""
310214501Srpaulo}
311214501Srpaulo{
312214501Srpaulo	if ( ! found )
313252726Srpaulo	{
314214501Srpaulo		if ( ! match($0, regex) ) next
315214501Srpaulo
316214501Srpaulo		found = 1
317214501Srpaulo		sub(/^[^#]*(#[[:space:]]*)?/, "")
318214501Srpaulo		buffer = $0
319214501Srpaulo		next
320214501Srpaulo	}
321214501Srpaulo
322214501Srpaulo	if ( !/^[[:space:]]*#/ ||
323214501Srpaulo	      /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
324214501Srpaulo	      /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
325214501Srpaulo	      /^[[:space:]]*$/ ) exit
326214501Srpaulo
327214501Srpaulo	sub(/(.*#)*[[:space:]]*/, "")
328214501Srpaulo	buffer = buffer" "$0
329214501Srpaulo}
330214501SrpauloEND {
331214501Srpaulo	# Clean up the buffer
332214501Srpaulo	sub(/^[[:space:]]*/, "", buffer)
333214501Srpaulo	sub(/[[:space:]]*$/, "", buffer)
334214501Srpaulo
335214501Srpaulo	print buffer
336214501Srpaulo	exit ! found
337214501Srpaulo}
338214501Srpaulo'
339214501Srpaulof_sysrc_desc()
340214501Srpaulo{
341214501Srpaulo	awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
342214501Srpaulo}
343214501Srpaulo
344214501Srpaulo# f_sysrc_set $varname $new_value
345214501Srpaulo#
346214501Srpaulo# Change a setting in the system configuration files (edits the files in-place
347214501Srpaulo# to change the value in the last assignment to the variable). If the variable
348214501Srpaulo# does not appear in the source file, it is appended to the end of the primary
349214501Srpaulo# system configuration file `/etc/rc.conf'.
350214501Srpaulo#
351214501Srpaulo# This function is a two-parter. Below is the awk(1) portion of the function,
352214501Srpaulo# afterward is the sh(1) function which utilizes the below awk script.
353214501Srpaulo#
354214501Srpaulof_sysrc_set_awk='
355214501Srpaulo# Variables that should be defined on the invocation line:
356214501Srpaulo# 	-v varname="varname"
357214501Srpaulo# 	-v new_value="new_value"
358214501Srpaulo#
359214501SrpauloBEGIN {
360214501Srpaulo	regex = "^[[:space:]]*"varname"="
361214501Srpaulo	found = retval = 0
362214501Srpaulo}
363214501Srpaulo{
364214501Srpaulo	# If already found... just spew
365214501Srpaulo	if ( found ) { print; next }
366214501Srpaulo
367214501Srpaulo	# Does this line match an assignment to our variable?
368214501Srpaulo	if ( ! match($0, regex) ) { print; next }
369214501Srpaulo
370214501Srpaulo	# Save important match information
371214501Srpaulo	found = 1
372214501Srpaulo	matchlen = RSTART + RLENGTH - 1
373214501Srpaulo
374214501Srpaulo	# Store the value text for later munging
375214501Srpaulo	value = substr($0, matchlen + 1, length($0) - matchlen)
376214501Srpaulo
377214501Srpaulo	# Store the first character of the value
378214501Srpaulo	t1 = t2 = substr(value, 0, 1)
379214501Srpaulo
380214501Srpaulo	# Assignment w/ back-ticks, expression, or misc.
381214501Srpaulo	# We ignore these since we did not generate them
382214501Srpaulo	#
383214501Srpaulo	if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
384214501Srpaulo
385214501Srpaulo	# Assignment w/ single-quoted value
386214501Srpaulo	else if ( t1 == "'\''" ) {
387214501Srpaulo		sub(/^'\''[^'\'']*/, "", value)
388214501Srpaulo		if ( length(value) == 0 ) t2 = ""
389214501Srpaulo		sub(/^'\''/, "", value)
390214501Srpaulo	}
391214501Srpaulo
392214501Srpaulo	# Assignment w/ double-quoted value
393214501Srpaulo	else if ( t1 == "\"" ) {
394214501Srpaulo		sub(/^"(.*\\\\+")*[^"]*/, "", value)
395214501Srpaulo		if ( length(value) == 0 ) t2 = ""
396214501Srpaulo		sub(/^"/, "", value)
397214501Srpaulo	}
398214501Srpaulo
399214501Srpaulo	# Assignment w/ non-quoted value
400214501Srpaulo	else if ( t1 ~ /[^[:space:];]/ ) {
401214501Srpaulo		t1 = t2 = "\""
402214501Srpaulo		sub(/^[^[:space:]]*/, "", value)
403214501Srpaulo	}
404214501Srpaulo
405214501Srpaulo	# Null-assignment
406214501Srpaulo	else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
407214501Srpaulo
408214501Srpaulo	printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
409214501Srpaulo		t1, new_value, t2, value
410214501Srpaulo}
411214501SrpauloEND { exit retval }
412214501Srpaulo'
413214501Srpaulof_sysrc_set()
414214501Srpaulo{
415214501Srpaulo	local varname="$1" new_value="$2"
416214501Srpaulo
417214501Srpaulo	# Check arguments
418214501Srpaulo	[ "$varname" ] || return $FAILURE
419214501Srpaulo
420214501Srpaulo	#
421214501Srpaulo	# Find which rc.conf(5) file contains the last-assignment
422252726Srpaulo	#
423252726Srpaulo	local not_found=
424252726Srpaulo	local file="$( f_sysrc_find "$varname" )"
425252726Srpaulo	if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
426252726Srpaulo		#
427252726Srpaulo		# We either got a null response (not found) or the variable
428214501Srpaulo		# was only found in the rc.conf(5) defaults. In either case,
429214501Srpaulo		# let's instead modify the first file from $rc_conf_files.
430214501Srpaulo		#
431214501Srpaulo
432214501Srpaulo		not_found=1
433214501Srpaulo
434252726Srpaulo		#
435252726Srpaulo		# If RC_CONFS is defined, use $RC_CONFS
436252726Srpaulo		# rather than $rc_conf_files.
437214501Srpaulo		#
438214501Srpaulo		if [ "$RC_CONFS" ]; then
439214501Srpaulo			file="${RC_CONFS%%[$IFS]*}"
440252726Srpaulo		else
441252726Srpaulo			file=$( f_sysrc_get rc_conf_files )
442252726Srpaulo			file="${file%%[$IFS]*}"
443214501Srpaulo		fi
444214501Srpaulo	fi
445214501Srpaulo
446214501Srpaulo	#
447214501Srpaulo	# If not found, append new value to last file and return.
448214501Srpaulo	#
449214501Srpaulo	if [ "$not_found" ]; then
450214501Srpaulo		echo "$varname=\"$new_value\"" >> "$file"
451214501Srpaulo		return $?
452214501Srpaulo	fi
453214501Srpaulo
454214501Srpaulo	#
455252726Srpaulo	# Perform sanity checks.
456252726Srpaulo	#
457252726Srpaulo	if [ ! -w "$file" ]; then
458252726Srpaulo		f_err "$msg_cannot_create_permission_denied\n" \
459214501Srpaulo		      "$pgm" "$file"
460214501Srpaulo		return $FAILURE
461252726Srpaulo	fi
462252726Srpaulo
463252726Srpaulo	#
464214501Srpaulo	# Create a new temporary file to write to.
465214501Srpaulo	#
466214501Srpaulo	local tmpfile="$( mktemp -t "$pgm" )"
467214501Srpaulo	[ "$tmpfile" ] || return $FAILURE
468214501Srpaulo
469214501Srpaulo	#
470214501Srpaulo	# Fixup permissions (else we're in for a surprise, as mktemp(1) creates
471214501Srpaulo	# the temporary file with 0600 permissions, and if we simply mv(1) the
472214501Srpaulo	# temporary file over the destination, the destination will inherit the
473214501Srpaulo	# permissions from the temporary file).
474214501Srpaulo	#
475214501Srpaulo	local mode
476214501Srpaulo	mode=$( stat -f '%#Lp' "$file" 2> /dev/null )
477214501Srpaulo	f_quietly chmod "${mode:-0644}" "$tmpfile"
478214501Srpaulo
479214501Srpaulo	#
480214501Srpaulo	# Fixup ownership. The destination file _is_ writable (we tested
481214501Srpaulo	# earlier above). However, this will fail if we don't have sufficient
482214501Srpaulo	# permissions (so we throw stderr into the bit-bucket).
483214501Srpaulo	#
484214501Srpaulo	local owner
485214501Srpaulo	owner=$( stat -f '%u:%g' "$file" 2> /dev/null )
486214501Srpaulo	f_quietly chown "${owner:-root:wheel}" "$tmpfile"
487214501Srpaulo
488214501Srpaulo	#
489214501Srpaulo	# Operate on the matching file, replacing only the last occurrence.
490214501Srpaulo	#
491214501Srpaulo	local new_contents retval
492214501Srpaulo	new_contents=$( tail -r $file 2> /dev/null )
493214501Srpaulo	new_contents=$( echo "$new_contents" | awk -v varname="$varname" \
494214501Srpaulo		-v new_value="$new_value" "$f_sysrc_set_awk" )
495214501Srpaulo	retval=$?
496214501Srpaulo
497214501Srpaulo	#
498214501Srpaulo	# Write the temporary file contents.
499214501Srpaulo	#
500252726Srpaulo	echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE
501252726Srpaulo	if [ $retval -ne $SUCCESS ]; then
502252726Srpaulo		echo "$varname=\"$new_value\"" >> "$tmpfile"
503252726Srpaulo	fi
504252726Srpaulo
505252726Srpaulo	#
506214501Srpaulo	# Taint-check our results.
507214501Srpaulo	#
508214501Srpaulo	if ! /bin/sh -n "$tmpfile"; then
509214501Srpaulo		f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
510214501Srpaulo		rm -f "$tmpfile"
511214501Srpaulo		return $FAILURE
512214501Srpaulo	fi
513214501Srpaulo
514214501Srpaulo	#
515214501Srpaulo	# Finally, move the temporary file into place.
516214501Srpaulo	#
517214501Srpaulo	mv "$tmpfile" "$file"
518214501Srpaulo}
519214501Srpaulo
520214501Srpaulo# f_sysrc_delete $varname
521214501Srpaulo#
522214501Srpaulo# Remove a setting from the system configuration files (edits files in-place).
523214501Srpaulo# Deletes all assignments to the given variable in all config files. If the
524214501Srpaulo# `-f file' option is passed, the removal is restricted to only those files
525214501Srpaulo# specified, otherwise the system collection of rc_conf_files is used.
526214501Srpaulo#
527214501Srpaulo# This function is a two-parter. Below is the awk(1) portion of the function,
528214501Srpaulo# afterward is the sh(1) function which utilizes the below awk script.
529214501Srpaulo#
530214501Srpaulof_sysrc_delete_awk='
531214501Srpaulo# Variables that should be defined on the invocation line:
532214501Srpaulo# 	-v varname="varname"
533214501Srpaulo#
534214501SrpauloBEGIN {
535214501Srpaulo	regex = "^[[:space:]]*"varname"="
536214501Srpaulo	found = 0
537214501Srpaulo}
538214501Srpaulo{
539214501Srpaulo	if ( $0 ~ regex )
540214501Srpaulo		found = 1
541214501Srpaulo	else
542214501Srpaulo		print
543214501Srpaulo}
544214501SrpauloEND { exit ! found }
545214501Srpaulo'
546214501Srpaulof_sysrc_delete()
547214501Srpaulo{
548214501Srpaulo	local varname="$1"
549214501Srpaulo	local file
550214501Srpaulo
551214501Srpaulo	# Check arguments
552214501Srpaulo	[ "$varname" ] || return $FAILURE
553214501Srpaulo
554214501Srpaulo	#
555214501Srpaulo	# Operate on each of the specified files
556214501Srpaulo	#
557214501Srpaulo	for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do
558214501Srpaulo		[ -e "$file" ] || continue
559214501Srpaulo
560214501Srpaulo		#
561214501Srpaulo		# Create a new temporary file to write to.
562214501Srpaulo		#
563214501Srpaulo		local tmpfile="$( mktemp -t "$pgm" )"
564214501Srpaulo		[ "$tmpfile" ] || return $FAILURE
565214501Srpaulo
566214501Srpaulo		#
567214501Srpaulo		# Fixup permissions and ownership (mktemp(1) defaults to 0600
568214501Srpaulo		# permissions) to instead match the destination file.
569252726Srpaulo		#
570252726Srpaulo		local mode owner
571214501Srpaulo		mode=$( stat -f '%#Lp' "$file" 2> /dev/null )
572214501Srpaulo		owner=$( stat -f '%u:%g' "$file" 2> /dev/null )
573214501Srpaulo		f_quietly chmod "${mode:-0644}" "$tmpfile"
574214501Srpaulo		f_quietly chown "${owner:-root:wheel}" "$tmpfile"
575214501Srpaulo
576214501Srpaulo		#
577214501Srpaulo		# Operate on the file, removing all occurrences, saving the
578214501Srpaulo		# output in our temporary file.
579214501Srpaulo		#
580214501Srpaulo		awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
581214501Srpaulo			> "$tmpfile"
582214501Srpaulo		if [ $? -ne $SUCCESS ]; then
583214501Srpaulo			# The file didn't contain any assignments
584214501Srpaulo			rm -f "$tmpfile"
585214501Srpaulo			continue
586214501Srpaulo		fi
587214501Srpaulo
588214501Srpaulo		#
589214501Srpaulo		# Taint-check our results.
590214501Srpaulo		#
591214501Srpaulo		if ! /bin/sh -n "$tmpfile"; then
592214501Srpaulo			f_err "$msg_previous_syntax_errors\n" \
593214501Srpaulo			      "$pgm" "$file"
594214501Srpaulo			rm -f "$tmpfile"
595214501Srpaulo			return $FAILURE
596214501Srpaulo		fi
597214501Srpaulo
598214501Srpaulo		#
599214501Srpaulo		# Perform sanity checks
600214501Srpaulo		#
601214501Srpaulo		if [ ! -w "$file" ]; then
602214501Srpaulo			f_err "$msg_permission_denied\n" "$pgm" "$file"
603214501Srpaulo			rm -f "$tmpfile"
604214501Srpaulo			return $FAILURE
605214501Srpaulo		fi
606214501Srpaulo
607214501Srpaulo		#
608214501Srpaulo		# Finally, move the temporary file into place.
609214501Srpaulo		#
610214501Srpaulo		mv "$tmpfile" "$file"
611214501Srpaulo	done
612214501Srpaulo}
613214501Srpaulo
614214501Srpaulofi # ! $_SYSRC_SUBR
615214501Srpaulo