1238438Sdteskeif [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
2238438Sdteske#
3290693Sdteske# Copyright (c) 2006-2015 Devin Teske
4252982Sdteske# All rights reserved.
5238438Sdteske#
6238438Sdteske# Redistribution and use in source and binary forms, with or without
7238438Sdteske# modification, are permitted provided that the following conditions
8238438Sdteske# are met:
9238438Sdteske# 1. Redistributions of source code must retain the above copyright
10238438Sdteske#    notice, this list of conditions and the following disclaimer.
11238438Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12238438Sdteske#    notice, this list of conditions and the following disclaimer in the
13238438Sdteske#    documentation and/or other materials provided with the distribution.
14238438Sdteske#
15238438Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16252987Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17238438Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18238438Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19238438Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20240797Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21238438Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22238438Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23238438Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24238438Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25238438Sdteske# SUCH DAMAGE.
26238438Sdteske#
27238438Sdteske# $FreeBSD: releng/11.0/usr.sbin/bsdconfig/share/sysrc.subr 290693 2015-11-12 00:30:33Z dteske $
28238438Sdteske#
29238438Sdteske############################################################ INCLUDES
30238438Sdteske
31240684SdteskeBSDCFG_SHARE="/usr/share/bsdconfig"
32241149Sdteske[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
33240684Sdteske
34238438SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig"
35244675Sdteskeif [ ! "$_SYSRC_JAILED" ]; then
36244675Sdteske	f_dprintf "%s: loading includes..." sysrc.subr
37244675Sdteske	f_include_lang $BSDCFG_LIBE/include/messages.subr
38244675Sdteskefi
39238438Sdteske
40238438Sdteske############################################################ CONFIGURATION
41238438Sdteske
42238438Sdteske#
43238438Sdteske# Standard pathnames (inherit values from shell if available)
44238438Sdteske#
45238438Sdteske: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
46238438Sdteske
47238438Sdteske############################################################ GLOBALS
48238438Sdteske
49238438Sdteske#
50238438Sdteske# Global exit status variables
51238438Sdteske#
52238438SdteskeSUCCESS=0
53238438SdteskeFAILURE=1
54238438Sdteske
55241019Sdteske#
56241019Sdteske# Valid characters that can appear in an sh(1) variable name
57241019Sdteske#
58241019Sdteske# Please note that the character ranges A-Z and a-z should be avoided because
59241019Sdteske# these can include accent characters (which are not valid in a variable name).
60241019Sdteske# For example, A-Z matches any character that sorts after A but before Z,
61241019Sdteske# including A and Z. Although ASCII order would make more sense, that is not
62241019Sdteske# how it works.
63241019Sdteske#
64241019SdteskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
65241019Sdteske
66238438Sdteske############################################################ FUNCTIONS
67238438Sdteske
68238438Sdteske# f_clean_env [ --except $varname ... ]
69238438Sdteske#
70238438Sdteske# Unset all environment variables in the current scope. An optional list of
71238438Sdteske# arguments can be passed, indicating which variables to avoid unsetting; the
72238438Sdteske# `--except' is required to enable the exclusion-list as the remainder of
73238438Sdteske# positional arguments.
74238438Sdteske#
75238438Sdteske# Be careful not to call this in a shell that you still expect to perform
76238438Sdteske# $PATH expansion in, because this will blow $PATH away. This is best used
77238438Sdteske# within a sub-shell block "(...)" or "$(...)" or "`...`".
78238438Sdteske#
79238438Sdteskef_clean_env()
80238438Sdteske{
81238438Sdteske	local var arg except=
82238438Sdteske
83238438Sdteske	#
84238438Sdteske	# Should we process an exclusion-list?
85238438Sdteske	#
86238438Sdteske	if [ "$1" = "--except" ]; then
87238438Sdteske		except=1
88238438Sdteske		shift 1
89238438Sdteske	fi
90238438Sdteske
91238438Sdteske	#
92238438Sdteske	# Loop over a list of variable names from set(1) built-in.
93238438Sdteske	#
94238438Sdteske	for var in $( set | awk -F= \
95238438Sdteske		'/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
96238438Sdteske		| grep -v '^except$'
97238438Sdteske	); do
98238438Sdteske		#
99238438Sdteske		# In POSIX bourne-shell, attempting to unset(1) OPTIND results
100238438Sdteske		# in "unset: Illegal number:" and causes abrupt termination.
101238438Sdteske		#
102238438Sdteske		[ "$var" = OPTIND ] && continue
103238438Sdteske
104238438Sdteske		#
105238438Sdteske		# Process the exclusion-list?
106238438Sdteske		#
107238438Sdteske		if [ "$except" ]; then
108238438Sdteske			for arg in "$@" ""; do
109238438Sdteske				[ "$var" = "$arg" ] && break
110238438Sdteske			done
111238438Sdteske			[ "$arg" ] && continue
112238438Sdteske		fi
113238438Sdteske
114238438Sdteske		unset "$var"
115238438Sdteske	done
116238438Sdteske}
117238438Sdteske
118238438Sdteske# f_sysrc_get $varname
119238438Sdteske#
120238438Sdteske# Get a system configuration setting from the collection of system-
121258029Sdteske# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and
122258029Sdteske# /etc/rc.conf.local)
123238438Sdteske#
124238438Sdteske# NOTE: Additional shell parameter-expansion formats are supported. For
125238438Sdteske# example, passing an argument of "hostname%%.*" (properly quoted) will
126238438Sdteske# return the hostname up to (but not including) the first `.' (see sh(1),
127238438Sdteske# "Parameter Expansion" for more information on additional formats).
128238438Sdteske#
129238438Sdteskef_sysrc_get()
130238438Sdteske{
131238438Sdteske	# Sanity check
132238438Sdteske	[ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
133238438Sdteske
134238438Sdteske	# Taint-check variable name
135238438Sdteske	case "$1" in
136238438Sdteske	[0-9]*)
137238438Sdteske		# Don't expand possible positional parameters
138252178Sdteske		return $FAILURE ;;
139238438Sdteske	*)
140238438Sdteske		[ "$1" ] || return $FAILURE
141238438Sdteske	esac
142238438Sdteske
143238438Sdteske	( # Execute within sub-shell to protect parent environment
144238438Sdteske
145238438Sdteske		#
146238438Sdteske		# Clear the environment of all variables, preventing the
147238438Sdteske		# expansion of normals such as `PS1', `TERM', etc.
148238438Sdteske		#
149241042Sdteske		f_clean_env --except IFS RC_CONFS RC_DEFAULTS
150238438Sdteske
151240783Sdteske		. "$RC_DEFAULTS" > /dev/null 2>&1
152238438Sdteske
153238438Sdteske		unset RC_DEFAULTS
154238438Sdteske			# no longer needed
155238438Sdteske
156238438Sdteske		#
157238438Sdteske		# If the query is for `rc_conf_files' then store the value that
158238438Sdteske		# we inherited from sourcing RC_DEFAULTS (above) so that we may
159238438Sdteske		# conditionally restore this value after source_rc_confs in the
160238438Sdteske		# event that RC_CONFS does not customize the value.
161238438Sdteske		#
162238438Sdteske		if [ "$1" = "rc_conf_files" ]; then
163238438Sdteske			_rc_conf_files="$rc_conf_files"
164238438Sdteske		fi
165238438Sdteske
166238438Sdteske		#
167238438Sdteske		# If RC_CONFS is defined, set $rc_conf_files to an explicit
168238438Sdteske		# value, modifying the default behavior of source_rc_confs().
169238438Sdteske		#
170240798Sdteske		if [ "${RC_CONFS+set}" ]; then
171238438Sdteske			rc_conf_files="$RC_CONFS"
172238438Sdteske			_rc_confs_set=1
173238438Sdteske		fi
174238438Sdteske
175240783Sdteske		source_rc_confs > /dev/null 2>&1
176238438Sdteske
177238438Sdteske		#
178238438Sdteske		# If the query was for `rc_conf_files' AND after calling
179240797Sdteske		# source_rc_confs the value has not changed, then we should
180238438Sdteske		# restore the value to the one inherited from RC_DEFAULTS
181238438Sdteske		# before performing the final query (preventing us from
182238438Sdteske		# returning what was set via RC_CONFS when the intent was
183238438Sdteske		# instead to query the value from the file(s) specified).
184238438Sdteske		#
185238438Sdteske		if [ "$1" = "rc_conf_files" -a \
186238438Sdteske		     "$_rc_confs_set" -a \
187238438Sdteske		     "$rc_conf_files" = "$RC_CONFS" \
188238438Sdteske		]; then
189238438Sdteske			rc_conf_files="$_rc_conf_files"
190238438Sdteske			unset _rc_conf_files
191238438Sdteske			unset _rc_confs_set
192238438Sdteske		fi
193238438Sdteske
194238438Sdteske		unset RC_CONFS
195238438Sdteske			# no longer needed
196238438Sdteske
197238438Sdteske		#
198238438Sdteske		# This must be the last functional line for both the sub-shell
199238438Sdteske		# and the function to preserve the return status from formats
200238438Sdteske		# such as "${varname?}" and "${varname:?}" (see "Parameter
201238438Sdteske		# Expansion" in sh(1) for more information).
202238438Sdteske		#
203240783Sdteske		eval echo '"${'"$1"'}"' 2> /dev/null
204238438Sdteske	)
205238438Sdteske}
206238438Sdteske
207290693Sdteske# f_sysrc_service_configs [-a|-p] $name [$var_to_set]
208290693Sdteske#
209290693Sdteske# Get a list of optional `rc.conf.d' entries sourced by system `rc.d' script
210290693Sdteske# $name (see rc.subr(8) for additional information on `rc.conf.d'). If $name
211290693Sdteske# exists in `/etc/rc.d' or $local_startup directories and is an rc(8) script
212290693Sdteske# the result is a space separated list of `rc.conf.d' entries sourced by the
213290693Sdteske# $name `rc.d' script. Otherwise, if $name exists as a binary `rc.d' script,
214290693Sdteske# the result is ``/etc/rc.conf.d/$name /usr/local/etc/rc.conf.d/$name''. The
215290693Sdteske# result is NULL if $name does not exist.
216290693Sdteske#
217290693Sdteske# If $var_to_set is missing or NULL, output is to standard out. Returns success
218290693Sdteske# if $name was found, failure otherwise.
219290693Sdteske#
220290693Sdteske# If `-a' flag is given and $var_to_set is non-NULL, append result to value of
221290693Sdteske# $var_to_set rather than overwriting current contents.
222290693Sdteske#
223290693Sdteske# If `-p' flag is given and $var_to_set is non-NULL, prepend result to value of
224290693Sdteske# $var_to_set rather than overwriting current contents.
225290693Sdteske#
226290693Sdteske# NB: The `-a' and `-p' option flags are mutually exclusive.
227290693Sdteske#
228290693Sdteskef_sysrc_service_configs()
229290693Sdteske{
230290693Sdteske	local OPTIND=1 OPTARG __flag __append= __prepend=
231290693Sdteske	local __local_startup __dir __spath __stype __names=
232290693Sdteske
233290693Sdteske	while getopts ap __flag; do
234290693Sdteske		case "$__flag" in
235290693Sdteske		a) __append=1 __prepend= ;;
236290693Sdteske		p) __prepend=1 __append= ;;
237290693Sdteske		esac
238290693Sdteske	done
239290693Sdteske	shift $(( $OPTIND - 1 ))
240290693Sdteske
241290693Sdteske	[ $# -gt 0 ] || return $FAILURE
242290693Sdteske	local __sname="$1" __var_to_set="$2"
243290693Sdteske
244290693Sdteske	__local_startup=$( f_sysrc_get local_startup )
245290693Sdteske	for __dir in /etc/rc.d $__local_startup; do
246290693Sdteske		__spath="$__dir/$__sname"
247290693Sdteske		[ -f "$__spath" -a -x "$__spath" ] || __spath= continue
248290693Sdteske		break
249290693Sdteske	done
250290693Sdteske	[ "$__spath" ] || return $FAILURE
251290693Sdteske
252290693Sdteske	__stype=$( file -b "$__spath" 2> /dev/null )
253290693Sdteske	case "$__stype" in
254290693Sdteske	*"shell script"*)
255290693Sdteske		__names=$( exec 9<&1 1>&- 2>&-
256290693Sdteske			last_name=
257290693Sdteske			print_name() {
258290693Sdteske				local name="$1"
259290693Sdteske				[ "$name" = "$last_name" ] && return
260290693Sdteske				echo "$name" >&9
261290693Sdteske				last_name="$name"
262290693Sdteske			}
263290693Sdteske			eval "$( awk '{
264290693Sdteske				gsub(/load_rc_config /, "print_name ")
265290693Sdteske				gsub(/run_rc_command /, ": ")
266290693Sdteske				print
267290693Sdteske			}' "$__spath" )"
268290693Sdteske		) ;;
269290693Sdteske	*)
270290693Sdteske		__names="$__sname"
271290693Sdteske	esac
272290693Sdteske
273290693Sdteske	local __name __test_path __configs=
274290693Sdteske	for __name in $__names; do
275290693Sdteske		for __dir in /etc/rc.d $__local_startup; do
276290693Sdteske			__test_path="${__dir%/rc.d}/rc.conf.d/$__name"
277290693Sdteske			[ -d "$__test_path" ] ||
278290693Sdteske				__configs="$__configs $__test_path" continue
279290693Sdteske			for __test_path in "$__test_path"/*; do
280290693Sdteske				[ -f "$__test_path" ] || continue
281290693Sdteske				__configs="$__configs $__test_path"
282290693Sdteske			done	
283290693Sdteske		done
284290693Sdteske	done
285290693Sdteske	__configs="${__configs# }"
286290693Sdteske
287290693Sdteske	if [ "$__var_to_set" ]; then
288290693Sdteske		local __cur=
289290693Sdteske		[ "$__append" -o "$__prepend" ] &&
290290693Sdteske			f_getvar "$__var_to_set" __cur
291290693Sdteske		[ "$__append"  ] && __configs="$__cur{$__cur:+ }$__configs"
292290693Sdteske		[ "$__prepend" ] && __configs="$__configs${__cur:+ }$__cur"
293290693Sdteske		setvar "$__var_to_set" "$__configs"
294290693Sdteske	else
295290693Sdteske		echo "$__configs"
296290693Sdteske	fi
297290693Sdteske
298290693Sdteske	return $SUCCESS
299290693Sdteske}
300290693Sdteske
301238438Sdteske# f_sysrc_get_default $varname
302238438Sdteske#
303238438Sdteske# Get a system configuration default setting from the default rc.conf(5) file
304238438Sdteske# (or whatever RC_DEFAULTS points at).
305238438Sdteske#
306238438Sdteskef_sysrc_get_default()
307238438Sdteske{
308238438Sdteske	# Sanity check
309238438Sdteske	[ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
310238438Sdteske
311238438Sdteske	# Taint-check variable name
312238438Sdteske	case "$1" in
313238438Sdteske	[0-9]*)
314238438Sdteske		# Don't expand possible positional parameters
315252178Sdteske		return $FAILURE ;;
316238438Sdteske	*)
317238438Sdteske		[ "$1" ] || return $FAILURE
318238438Sdteske	esac
319238438Sdteske
320238438Sdteske	( # Execute within sub-shell to protect parent environment
321238438Sdteske
322238438Sdteske		#
323238438Sdteske		# Clear the environment of all variables, preventing the
324238438Sdteske		# expansion of normals such as `PS1', `TERM', etc.
325238438Sdteske		#
326238438Sdteske		f_clean_env --except RC_DEFAULTS
327238438Sdteske
328240783Sdteske		. "$RC_DEFAULTS" > /dev/null 2>&1
329238438Sdteske
330238438Sdteske		unset RC_DEFAULTS
331238438Sdteske			# no longer needed
332238438Sdteske
333238438Sdteske		#
334238438Sdteske		# This must be the last functional line for both the sub-shell
335238438Sdteske		# and the function to preserve the return status from formats
336238438Sdteske		# such as "${varname?}" and "${varname:?}" (see "Parameter
337238438Sdteske		# Expansion" in sh(1) for more information).
338238438Sdteske		#
339240783Sdteske		eval echo '"${'"$1"'}"' 2> /dev/null
340238438Sdteske	)
341238438Sdteske}
342238438Sdteske
343238438Sdteske# f_sysrc_find $varname
344238438Sdteske#
345238438Sdteske# Find which file holds the effective last-assignment to a given variable
346238438Sdteske# within the rc.conf(5) file(s).
347238438Sdteske#
348238438Sdteske# If the variable is found in any of the rc.conf(5) files, the function prints
349238438Sdteske# the filename it was found in and then returns success. Otherwise output is
350238438Sdteske# NULL and the function returns with error status.
351238438Sdteske#
352238438Sdteskef_sysrc_find()
353238438Sdteske{
354241019Sdteske	local varname="${1%%[!$VALID_VARNAME_CHARS]*}"
355238438Sdteske	local regex="^[[:space:]]*$varname="
356238438Sdteske	local rc_conf_files="$( f_sysrc_get rc_conf_files )"
357238438Sdteske	local conf_files=
358238438Sdteske	local file
359238438Sdteske
360238438Sdteske	# Check parameters
361241019Sdteske	case "$varname" in
362241019Sdteske	""|[0-9]*) return $FAILURE
363241019Sdteske	esac
364238438Sdteske
365238438Sdteske	#
366238438Sdteske	# If RC_CONFS is defined, set $rc_conf_files to an explicit
367238438Sdteske	# value, modifying the default behavior of source_rc_confs().
368238438Sdteske	#
369240863Sdteske	[ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS"
370238438Sdteske
371238438Sdteske	#
372238438Sdteske	# Reverse the order of files in rc_conf_files (the boot process sources
373238438Sdteske	# these in order, so we will search them in reverse-order to find the
374238438Sdteske	# last-assignment -- the one that ultimately effects the environment).
375238438Sdteske	#
376238438Sdteske	for file in $rc_conf_files; do
377238438Sdteske		conf_files="$file${conf_files:+ }$conf_files"
378238438Sdteske	done
379238438Sdteske
380238438Sdteske	#
381238438Sdteske	# Append the defaults file (since directives in the defaults file
382238438Sdteske	# indeed affect the boot process, we'll want to know when a directive
383238438Sdteske	# is found there).
384238438Sdteske	#
385238438Sdteske	conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
386238438Sdteske
387238438Sdteske	#
388238438Sdteske	# Find which file matches assignment to the given variable name.
389238438Sdteske	#
390238438Sdteske	for file in $conf_files; do
391238438Sdteske		[ -f "$file" -a -r "$file" ] || continue
392238438Sdteske		if grep -Eq "$regex" $file; then
393238438Sdteske			echo $file
394238438Sdteske			return $SUCCESS
395238438Sdteske		fi
396238438Sdteske	done
397238438Sdteske
398238438Sdteske	return $FAILURE # Not found
399238438Sdteske}
400238438Sdteske
401238438Sdteske# f_sysrc_desc $varname
402238438Sdteske#
403238438Sdteske# Attempts to return the comments associated with varname from the rc.conf(5)
404238438Sdteske# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
405238438Sdteske#
406238438Sdteske# Multi-line comments are joined together. Results are NULL if no description
407238438Sdteske# could be found.
408238438Sdteske#
409238438Sdteske# This function is a two-parter. Below is the awk(1) portion of the function,
410238438Sdteske# afterward is the sh(1) function which utilizes the below awk script.
411238438Sdteske#
412238438Sdteskef_sysrc_desc_awk='
413238438Sdteske# Variables that should be defined on the invocation line:
414238438Sdteske# 	-v varname="varname"
415238438Sdteske#
416238438SdteskeBEGIN {
417238438Sdteske	regex = "^[[:space:]]*"varname"="
418238438Sdteske	found = 0
419238438Sdteske	buffer = ""
420238438Sdteske}
421238438Sdteske{
422238438Sdteske	if ( ! found )
423238438Sdteske	{
424238438Sdteske		if ( ! match($0, regex) ) next
425238438Sdteske
426238438Sdteske		found = 1
427238438Sdteske		sub(/^[^#]*(#[[:space:]]*)?/, "")
428238438Sdteske		buffer = $0
429238438Sdteske		next
430238438Sdteske	}
431238438Sdteske
432238438Sdteske	if ( !/^[[:space:]]*#/ ||
433238438Sdteske	      /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
434238438Sdteske	      /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
435238438Sdteske	      /^[[:space:]]*$/ ) exit
436238438Sdteske
437238438Sdteske	sub(/(.*#)*[[:space:]]*/, "")
438238438Sdteske	buffer = buffer" "$0
439238438Sdteske}
440238438SdteskeEND {
441238438Sdteske	# Clean up the buffer
442238438Sdteske	sub(/^[[:space:]]*/, "", buffer)
443238438Sdteske	sub(/[[:space:]]*$/, "", buffer)
444238438Sdteske
445238438Sdteske	print buffer
446238438Sdteske	exit ! found
447238438Sdteske}
448238438Sdteske'
449238438Sdteskef_sysrc_desc()
450238438Sdteske{
451238438Sdteske	awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
452238438Sdteske}
453238438Sdteske
454238438Sdteske# f_sysrc_set $varname $new_value
455238438Sdteske#
456238438Sdteske# Change a setting in the system configuration files (edits the files in-place
457238438Sdteske# to change the value in the last assignment to the variable). If the variable
458238438Sdteske# does not appear in the source file, it is appended to the end of the primary
459238438Sdteske# system configuration file `/etc/rc.conf'.
460238438Sdteske#
461238438Sdteske# This function is a two-parter. Below is the awk(1) portion of the function,
462238438Sdteske# afterward is the sh(1) function which utilizes the below awk script.
463238438Sdteske#
464238438Sdteskef_sysrc_set_awk='
465238438Sdteske# Variables that should be defined on the invocation line:
466238438Sdteske# 	-v varname="varname"
467238438Sdteske# 	-v new_value="new_value"
468238438Sdteske#
469238438SdteskeBEGIN {
470238438Sdteske	regex = "^[[:space:]]*"varname"="
471238438Sdteske	found = retval = 0
472238438Sdteske}
473238438Sdteske{
474238438Sdteske	# If already found... just spew
475238438Sdteske	if ( found ) { print; next }
476238438Sdteske
477238438Sdteske	# Does this line match an assignment to our variable?
478238438Sdteske	if ( ! match($0, regex) ) { print; next }
479238438Sdteske
480238438Sdteske	# Save important match information
481238438Sdteske	found = 1
482238438Sdteske	matchlen = RSTART + RLENGTH - 1
483238438Sdteske
484238438Sdteske	# Store the value text for later munging
485238438Sdteske	value = substr($0, matchlen + 1, length($0) - matchlen)
486238438Sdteske
487238438Sdteske	# Store the first character of the value
488238438Sdteske	t1 = t2 = substr(value, 0, 1)
489238438Sdteske
490238438Sdteske	# Assignment w/ back-ticks, expression, or misc.
491238438Sdteske	# We ignore these since we did not generate them
492238438Sdteske	#
493238438Sdteske	if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
494238438Sdteske
495238438Sdteske	# Assignment w/ single-quoted value
496238438Sdteske	else if ( t1 == "'\''" ) {
497238438Sdteske		sub(/^'\''[^'\'']*/, "", value)
498238438Sdteske		if ( length(value) == 0 ) t2 = ""
499238438Sdteske		sub(/^'\''/, "", value)
500238438Sdteske	}
501238438Sdteske
502238438Sdteske	# Assignment w/ double-quoted value
503238438Sdteske	else if ( t1 == "\"" ) {
504238438Sdteske		sub(/^"(.*\\\\+")*[^"]*/, "", value)
505238438Sdteske		if ( length(value) == 0 ) t2 = ""
506238438Sdteske		sub(/^"/, "", value)
507238438Sdteske	}
508238438Sdteske
509238438Sdteske	# Assignment w/ non-quoted value
510238438Sdteske	else if ( t1 ~ /[^[:space:];]/ ) {
511238438Sdteske		t1 = t2 = "\""
512238438Sdteske		sub(/^[^[:space:]]*/, "", value)
513238438Sdteske	}
514238438Sdteske
515238438Sdteske	# Null-assignment
516238438Sdteske	else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
517238438Sdteske
518238438Sdteske	printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
519238438Sdteske		t1, new_value, t2, value
520238438Sdteske}
521238438SdteskeEND { exit retval }
522238438Sdteske'
523238438Sdteskef_sysrc_set()
524238438Sdteske{
525259054Sdteske	local funcname=f_sysrc_set
526238438Sdteske	local varname="$1" new_value="$2"
527238438Sdteske
528238438Sdteske	# Check arguments
529238438Sdteske	[ "$varname" ] || return $FAILURE
530238438Sdteske
531238438Sdteske	#
532238438Sdteske	# Find which rc.conf(5) file contains the last-assignment
533238438Sdteske	#
534238438Sdteske	local not_found=
535238438Sdteske	local file="$( f_sysrc_find "$varname" )"
536238438Sdteske	if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
537238438Sdteske		#
538238438Sdteske		# We either got a null response (not found) or the variable
539238438Sdteske		# was only found in the rc.conf(5) defaults. In either case,
540238438Sdteske		# let's instead modify the first file from $rc_conf_files.
541238438Sdteske		#
542238438Sdteske
543238438Sdteske		not_found=1
544238438Sdteske
545238438Sdteske		#
546238438Sdteske		# If RC_CONFS is defined, use $RC_CONFS
547238438Sdteske		# rather than $rc_conf_files.
548238438Sdteske		#
549240863Sdteske		if [ "${RC_CONFS+set}" ]; then
550238438Sdteske			file="${RC_CONFS%%[$IFS]*}"
551238438Sdteske		else
552241042Sdteske			file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
553238438Sdteske		fi
554238438Sdteske	fi
555238438Sdteske
556238438Sdteske	#
557238438Sdteske	# If not found, append new value to last file and return.
558238438Sdteske	#
559238438Sdteske	if [ "$not_found" ]; then
560238438Sdteske		echo "$varname=\"$new_value\"" >> "$file"
561238438Sdteske		return $?
562238438Sdteske	fi
563238438Sdteske
564238438Sdteske	#
565238438Sdteske	# Perform sanity checks.
566238438Sdteske	#
567238438Sdteske	if [ ! -w "$file" ]; then
568238438Sdteske		f_err "$msg_cannot_create_permission_denied\n" \
569238438Sdteske		      "$pgm" "$file"
570238438Sdteske		return $FAILURE
571238438Sdteske	fi
572238438Sdteske
573238438Sdteske	#
574238438Sdteske	# Create a new temporary file to write to.
575238438Sdteske	#
576259054Sdteske	local tmpfile
577259054Sdteske	if ! f_eval_catch -dk tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm"
578259054Sdteske	then
579259054Sdteske		echo "$tmpfile" >&2
580259054Sdteske		return $FAILURE
581259054Sdteske	fi
582238438Sdteske
583238438Sdteske	#
584238438Sdteske	# Fixup permissions (else we're in for a surprise, as mktemp(1) creates
585238438Sdteske	# the temporary file with 0600 permissions, and if we simply mv(1) the
586238438Sdteske	# temporary file over the destination, the destination will inherit the
587238438Sdteske	# permissions from the temporary file).
588238438Sdteske	#
589238438Sdteske	local mode
590259054Sdteske	f_eval_catch -dk mode $funcname stat 'stat -f "%%#Lp" "%s"' "$file" ||
591259054Sdteske		mode=0644
592259054Sdteske	f_eval_catch -d $funcname chmod 'chmod "%s" "%s"' "$mode" "$tmpfile"
593238438Sdteske
594238438Sdteske	#
595238438Sdteske	# Fixup ownership. The destination file _is_ writable (we tested
596238438Sdteske	# earlier above). However, this will fail if we don't have sufficient
597238438Sdteske	# permissions (so we throw stderr into the bit-bucket).
598238438Sdteske	#
599238438Sdteske	local owner
600259054Sdteske	f_eval_catch -dk owner $funcname stat \
601259054Sdteske		'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
602259054Sdteske	f_eval_catch -d $funcname chown 'chown "%s" "%s"' "$owner" "$tmpfile"
603238438Sdteske
604238438Sdteske	#
605238438Sdteske	# Operate on the matching file, replacing only the last occurrence.
606238438Sdteske	#
607238438Sdteske	local new_contents retval
608240783Sdteske	new_contents=$( tail -r $file 2> /dev/null )
609238438Sdteske	new_contents=$( echo "$new_contents" | awk -v varname="$varname" \
610238438Sdteske		-v new_value="$new_value" "$f_sysrc_set_awk" )
611238438Sdteske	retval=$?
612238438Sdteske
613238438Sdteske	#
614238438Sdteske	# Write the temporary file contents.
615238438Sdteske	#
616238438Sdteske	echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE
617238438Sdteske	if [ $retval -ne $SUCCESS ]; then
618238438Sdteske		echo "$varname=\"$new_value\"" >> "$tmpfile"
619238438Sdteske	fi
620238438Sdteske
621238438Sdteske	#
622238438Sdteske	# Taint-check our results.
623238438Sdteske	#
624259054Sdteske	if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"; then
625238438Sdteske		f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
626238438Sdteske		rm -f "$tmpfile"
627238438Sdteske		return $FAILURE
628238438Sdteske	fi
629238438Sdteske
630238438Sdteske	#
631238438Sdteske	# Finally, move the temporary file into place.
632238438Sdteske	#
633259054Sdteske	f_eval_catch -de $funcname mv 'mv "%s" "%s"' "$tmpfile" "$file"
634238438Sdteske}
635238438Sdteske
636238438Sdteske# f_sysrc_delete $varname
637238438Sdteske#
638238438Sdteske# Remove a setting from the system configuration files (edits files in-place).
639238438Sdteske# Deletes all assignments to the given variable in all config files. If the
640238438Sdteske# `-f file' option is passed, the removal is restricted to only those files
641238438Sdteske# specified, otherwise the system collection of rc_conf_files is used.
642238438Sdteske#
643238438Sdteske# This function is a two-parter. Below is the awk(1) portion of the function,
644238438Sdteske# afterward is the sh(1) function which utilizes the below awk script.
645238438Sdteske#
646238438Sdteskef_sysrc_delete_awk='
647238438Sdteske# Variables that should be defined on the invocation line:
648238438Sdteske# 	-v varname="varname"
649238438Sdteske#
650238438SdteskeBEGIN {
651238438Sdteske	regex = "^[[:space:]]*"varname"="
652238438Sdteske	found = 0
653238438Sdteske}
654238438Sdteske{
655238438Sdteske	if ( $0 ~ regex )
656238438Sdteske		found = 1
657238438Sdteske	else
658238438Sdteske		print
659238438Sdteske}
660238438SdteskeEND { exit ! found }
661238438Sdteske'
662238438Sdteskef_sysrc_delete()
663238438Sdteske{
664259054Sdteske	local funcname=f_sysrc_delete
665238438Sdteske	local varname="$1"
666238438Sdteske	local file
667238438Sdteske
668238438Sdteske	# Check arguments
669238438Sdteske	[ "$varname" ] || return $FAILURE
670238438Sdteske
671238438Sdteske	#
672238438Sdteske	# Operate on each of the specified files
673238438Sdteske	#
674259054Sdteske	local tmpfile
675240863Sdteske	for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do
676238438Sdteske		[ -e "$file" ] || continue
677238438Sdteske
678238438Sdteske		#
679238438Sdteske		# Create a new temporary file to write to.
680238438Sdteske		#
681259054Sdteske		if ! f_eval_catch -dk tmpfile $funcname mktemp \
682259054Sdteske			'mktemp -t "%s"' "$pgm"
683259054Sdteske		then
684259054Sdteske			echo "$tmpfile" >&2
685259054Sdteske			return $FAILURE
686259054Sdteske		fi
687238438Sdteske
688238438Sdteske		#
689238438Sdteske		# Fixup permissions and ownership (mktemp(1) defaults to 0600
690238438Sdteske		# permissions) to instead match the destination file.
691238438Sdteske		#
692238438Sdteske		local mode owner
693259054Sdteske		f_eval_catch -dk mode $funcname stat \
694259054Sdteske			'stat -f "%%#Lp" "%s"' "$file" || mode=0644
695259054Sdteske		f_eval_catch -dk owner $funcname stat \
696259054Sdteske			'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
697259054Sdteske		f_eval_catch -d $funcname chmod \
698259054Sdteske			'chmod "%s" "%s"' "$mode" "$tmpfile"
699259054Sdteske		f_eval_catch -d $funcname chown \
700259054Sdteske			'chown "%s" "%s"' "$owner" "$tmpfile"
701238438Sdteske
702238438Sdteske		#
703238438Sdteske		# Operate on the file, removing all occurrences, saving the
704238438Sdteske		# output in our temporary file.
705238438Sdteske		#
706238438Sdteske		awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
707238438Sdteske			> "$tmpfile"
708238438Sdteske		if [ $? -ne $SUCCESS ]; then
709238438Sdteske			# The file didn't contain any assignments
710238438Sdteske			rm -f "$tmpfile"
711238438Sdteske			continue
712238438Sdteske		fi
713238438Sdteske
714238438Sdteske		#
715238438Sdteske		# Taint-check our results.
716238438Sdteske		#
717259054Sdteske		if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"
718259054Sdteske		then
719238438Sdteske			f_err "$msg_previous_syntax_errors\n" \
720238438Sdteske			      "$pgm" "$file"
721238438Sdteske			rm -f "$tmpfile"
722238438Sdteske			return $FAILURE
723238438Sdteske		fi
724238438Sdteske
725238438Sdteske		#
726238438Sdteske		# Perform sanity checks
727238438Sdteske		#
728238438Sdteske		if [ ! -w "$file" ]; then
729238438Sdteske			f_err "$msg_permission_denied\n" "$pgm" "$file"
730238438Sdteske			rm -f "$tmpfile"
731238438Sdteske			return $FAILURE
732238438Sdteske		fi
733238438Sdteske
734238438Sdteske		#
735238438Sdteske		# Finally, move the temporary file into place.
736238438Sdteske		#
737259054Sdteske		f_eval_catch -de $funcname mv \
738259054Sdteske			'mv "%s" "%s"' "$tmpfile" "$file" || return $FAILURE
739238438Sdteske	done
740238438Sdteske}
741238438Sdteske
742244675Sdteske############################################################ MAIN
743244675Sdteske
744244675Sdteskef_dprintf "%s: Successfully loaded." sysrc.subr
745244675Sdteske
746238438Sdteskefi # ! $_SYSRC_SUBR
747