aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2026-04-15 09:41:09 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2026-09-14 13:42:27 +0000
commiteaeb5f29bc6f4ff31026b12bd2329699a066c68b (patch)
treee65114de02f474efa9449bb5b85e4d018e588233
parent42c69445ca336b13e27e3e5960ace344c64ae0eb (diff)
sysrc: make independant from bsdconfig(8)
MFC After: 1 week Approved by: dteske Reviewed by: dteske Differential Revision: https://reviews.freebsd.org/D59658
-rw-r--r--MAINTAINERS2
-rw-r--r--UPDATING5
-rw-r--r--packages/bsdconfig/bsdconfig.ucl3
-rw-r--r--usr.sbin/bsdconfig/share/sysrc.subr719
-rw-r--r--usr.sbin/sysrc/Makefile5
-rw-r--r--usr.sbin/sysrc/sysrc12
-rw-r--r--usr.sbin/sysrc/sysrc.subr898
7 files changed, 916 insertions, 728 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index fd514a085ae1..a695641a0dac 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -129,7 +129,7 @@ top(1) eadler Pre-commit review requested.
ufshci(4) jaeyoon Pre-commit review requested.
usr.sbin/bsdconfig dteske Pre-commit phabricator review requested.
usr.sbin/pkg pkg@ Please coordinate behavior or flag changes with pkg team.
-usr.sbin/sysrc dteske Pre-commit phabricator review requested. Keep in sync with bsdconfig(8) sysrc.subr.
+usr.sbin/sysrc dteske Pre-commit phabricator review requested.
vmm(4) #bhyve Pre-commit review requested via #bhyve phabricator group.
libvmmapi #bhyve Pre-commit review requested via #bhyve phabricator group.
usr.sbin/bhyve* #bhyve Pre-commit review requested via #bhyve phabricator group.
diff --git a/UPDATING b/UPDATING
index de5b9024832f..99c10a9adda1 100644
--- a/UPDATING
+++ b/UPDATING
@@ -27,6 +27,11 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 16.x IS SLOW:
world, or to merely disable the most expensive debugging functionality
at runtime, run "ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20260414:
+ sysrc(8) has been moved from the "bsdconfig" package to the "rc"
+ package and no longer depends on bsdconfig at runtime. For pkgbase
+ users, sysrc is now part of set-minimal instead of set-optional.
+
20260902:
struct if_rxd_info now carries a receive timestamp. iflib drivers
must be rebuilt. __FreeBSD_version has been bumped to 1600022.
diff --git a/packages/bsdconfig/bsdconfig.ucl b/packages/bsdconfig/bsdconfig.ucl
index ba34e5b96fe9..d712742108e5 100644
--- a/packages/bsdconfig/bsdconfig.ucl
+++ b/packages/bsdconfig/bsdconfig.ucl
@@ -21,7 +21,4 @@ comment = "System configuration utility"
desc = <<EOD
bsdconfig(8) provides a simple full-screen interface to manage system
configuration, including services, networking and disks.
-
-This package also provides sysrc(8), a command-line utility for managing
-the rc.conf(5) configuration.
EOD
diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr
index 5d2a3cd97f7c..eee489f4cc50 100644
--- a/usr.sbin/bsdconfig/share/sysrc.subr
+++ b/usr.sbin/bsdconfig/share/sysrc.subr
@@ -25,731 +25,22 @@ if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
# SUCH DAMAGE.
#
#
+# Compatibility shim: sources the canonical sysrc.subr from /usr/share/sysrc/
+# and loads bsdconfig-specific i18n messages afterward.
+#
############################################################ INCLUDES
BSDCFG_SHARE="/usr/share/bsdconfig"
[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
+f_include /usr/share/sysrc/sysrc.subr
+
BSDCFG_LIBE="/usr/libexec/bsdconfig"
if [ ! "$_SYSRC_JAILED" ]; then
f_dprintf "%s: loading includes..." sysrc.subr
f_include_lang $BSDCFG_LIBE/include/messages.subr
fi
-############################################################ CONFIGURATION
-
-#
-# Standard pathnames (inherit values from shell if available)
-#
-: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
-
-############################################################ GLOBALS
-
-#
-# Global exit status variables
-#
-SUCCESS=0
-FAILURE=1
-
-#
-# Valid characters that can appear in an sh(1) variable name
-#
-# Please note that the character ranges A-Z and a-z should be avoided because
-# these can include accent characters (which are not valid in a variable name).
-# For example, A-Z matches any character that sorts after A but before Z,
-# including A and Z. Although ASCII order would make more sense, that is not
-# how it works.
-#
-VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
-
-############################################################ FUNCTIONS
-
-# f_clean_env [ --except $varname ... ]
-#
-# Unset all environment variables in the current scope. An optional list of
-# arguments can be passed, indicating which variables to avoid unsetting; the
-# `--except' is required to enable the exclusion-list as the remainder of
-# positional arguments.
-#
-# Be careful not to call this in a shell that you still expect to perform
-# $PATH expansion in, because this will blow $PATH away. This is best used
-# within a sub-shell block "(...)" or "$(...)" or "`...`".
-#
-f_clean_env()
-{
- local var arg except=
-
- #
- # Should we process an exclusion-list?
- #
- if [ "$1" = "--except" ]; then
- except=1
- shift 1
- fi
-
- #
- # Loop over a list of variable names from set(1) built-in.
- #
- for var in $( set | awk -F= \
- '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
- | grep -v '^except$'
- ); do
- #
- # In POSIX bourne-shell, attempting to unset(1) OPTIND results
- # in "unset: Illegal number:" and causes abrupt termination.
- #
- [ "$var" = OPTIND ] && continue
-
- #
- # Process the exclusion-list?
- #
- if [ "$except" ]; then
- for arg in "$@" ""; do
- [ "$var" = "$arg" ] && break
- done
- [ "$arg" ] && continue
- fi
-
- unset "$var"
- done
-}
-
-# f_sysrc_get $varname
-#
-# Get a system configuration setting from the collection of system-
-# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and
-# /etc/rc.conf.local)
-#
-# NOTE: Additional shell parameter-expansion formats are supported. For
-# example, passing an argument of "hostname%%.*" (properly quoted) will
-# return the hostname up to (but not including) the first `.' (see sh(1),
-# "Parameter Expansion" for more information on additional formats).
-#
-f_sysrc_get()
-{
- # Sanity check
- [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
-
- # Taint-check variable name
- case "$1" in
- [0-9]*)
- # Don't expand possible positional parameters
- return $FAILURE ;;
- *)
- [ "$1" ] || return $FAILURE
- esac
-
- ( # Execute within sub-shell to protect parent environment
-
- #
- # Clear the environment of all variables, preventing the
- # expansion of normals such as `PS1', `TERM', etc.
- #
- f_clean_env --except IFS RC_CONFS RC_DEFAULTS
-
- . "$RC_DEFAULTS" > /dev/null 2>&1
-
- unset RC_DEFAULTS
- # no longer needed
-
- #
- # If the query is for `rc_conf_files' then store the value that
- # we inherited from sourcing RC_DEFAULTS (above) so that we may
- # conditionally restore this value after source_rc_confs in the
- # event that RC_CONFS does not customize the value.
- #
- if [ "$1" = "rc_conf_files" ]; then
- _rc_conf_files="$rc_conf_files"
- fi
-
- #
- # If RC_CONFS is defined, set $rc_conf_files to an explicit
- # value, modifying the default behavior of source_rc_confs().
- #
- if [ "${RC_CONFS+set}" ]; then
- rc_conf_files="$RC_CONFS"
- _rc_confs_set=1
- fi
-
- source_rc_confs > /dev/null 2>&1
-
- #
- # If the query was for `rc_conf_files' AND after calling
- # source_rc_confs the value has not changed, then we should
- # restore the value to the one inherited from RC_DEFAULTS
- # before performing the final query (preventing us from
- # returning what was set via RC_CONFS when the intent was
- # instead to query the value from the file(s) specified).
- #
- if [ "$1" = "rc_conf_files" -a \
- "$_rc_confs_set" -a \
- "$rc_conf_files" = "$RC_CONFS" \
- ]; then
- rc_conf_files="$_rc_conf_files"
- unset _rc_conf_files
- unset _rc_confs_set
- fi
-
- unset RC_CONFS
- # no longer needed
-
- #
- # This must be the last functional line for both the sub-shell
- # and the function to preserve the return status from formats
- # such as "${varname?}" and "${varname:?}" (see "Parameter
- # Expansion" in sh(1) for more information).
- #
- eval printf "'%s\\n'" '"${'"$1"'}"' 2> /dev/null
- )
-}
-
-# f_sysrc_service_configs [-a|-p] $name [$var_to_set]
-#
-# Get a list of optional `rc.conf.d' entries sourced by system `rc.d' script
-# $name (see rc.subr(8) for additional information on `rc.conf.d'). If $name
-# exists in `/etc/rc.d' or $local_startup directories and is an rc(8) script
-# the result is a space separated list of `rc.conf.d' entries sourced by the
-# $name `rc.d' script. Otherwise, if $name exists as a binary `rc.d' script,
-# the result is ``/etc/rc.conf.d/$name /usr/local/etc/rc.conf.d/$name''. The
-# result is NULL if $name does not exist.
-#
-# If $var_to_set is missing or NULL, output is to standard out. Returns success
-# if $name was found, failure otherwise.
-#
-# If `-a' flag is given and $var_to_set is non-NULL, append result to value of
-# $var_to_set rather than overwriting current contents.
-#
-# If `-p' flag is given and $var_to_set is non-NULL, prepend result to value of
-# $var_to_set rather than overwriting current contents.
-#
-# NB: The `-a' and `-p' option flags are mutually exclusive.
-#
-f_sysrc_service_configs()
-{
- local OPTIND=1 OPTARG __flag __append= __prepend=
- local __local_startup __dir __spath __stype __names=
-
- while getopts ap __flag; do
- case "$__flag" in
- a) __append=1 __prepend= ;;
- p) __prepend=1 __append= ;;
- esac
- done
- shift $(( $OPTIND - 1 ))
-
- [ $# -gt 0 ] || return $FAILURE
- local __sname="$1" __var_to_set="$2"
-
- __local_startup=$( f_sysrc_get local_startup )
- for __dir in /etc/rc.d $__local_startup; do
- __spath="$__dir/$__sname"
- [ -f "$__spath" -a -x "$__spath" ] || __spath= continue
- break
- done
- [ "$__spath" ] || return $FAILURE
-
- __stype=$( file -b "$__spath" 2> /dev/null )
- case "$__stype" in
- *"shell script"*)
- __names=$( exec 9<&1 1>&- 2>&-
- last_name=
- print_name() {
- local name="$1"
- case "$name" in
- ""|.|..|*/*|"$last_name") return ;;
- esac
- echo "$name" >&9
- last_name="$name"
- }
- eval "$( awk '{
- gsub(/load_rc_config /, "print_name ")
- gsub(/run_rc_command /, ": ")
- print
- }' "$__spath" )"
- ) ;;
- *)
- __names="$__sname"
- esac
-
- local __name __test_path __configs=
- for __name in $__names; do
- for __dir in /etc/rc.d $__local_startup; do
- __test_path="${__dir%/rc.d}/rc.conf.d/$__name"
- [ -d "$__test_path" ] ||
- __configs="$__configs $__test_path" continue
- for __test_path in "$__test_path"/*; do
- [ -f "$__test_path" ] || continue
- __configs="$__configs $__test_path"
- done
- done
- done
- __configs="${__configs# }"
-
- if [ "$__var_to_set" ]; then
- local __cur=
- [ "$__append" -o "$__prepend" ] &&
- f_getvar "$__var_to_set" __cur
- [ "$__append" ] && __configs="$__cur{$__cur:+ }$__configs"
- [ "$__prepend" ] && __configs="$__configs${__cur:+ }$__cur"
- setvar "$__var_to_set" "$__configs"
- else
- echo "$__configs"
- fi
-
- return $SUCCESS
-}
-
-# f_sysrc_get_default $varname
-#
-# Get a system configuration default setting from the default rc.conf(5) file
-# (or whatever RC_DEFAULTS points at).
-#
-f_sysrc_get_default()
-{
- # Sanity check
- [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
-
- # Taint-check variable name
- case "$1" in
- [0-9]*)
- # Don't expand possible positional parameters
- return $FAILURE ;;
- *)
- [ "$1" ] || return $FAILURE
- esac
-
- ( # Execute within sub-shell to protect parent environment
-
- #
- # Clear the environment of all variables, preventing the
- # expansion of normals such as `PS1', `TERM', etc.
- #
- f_clean_env --except RC_DEFAULTS
-
- . "$RC_DEFAULTS" > /dev/null 2>&1
-
- unset RC_DEFAULTS
- # no longer needed
-
- #
- # This must be the last functional line for both the sub-shell
- # and the function to preserve the return status from formats
- # such as "${varname?}" and "${varname:?}" (see "Parameter
- # Expansion" in sh(1) for more information).
- #
- eval printf "'%s\\n'" '"${'"$1"'}"' 2> /dev/null
- )
-}
-
-# f_sysrc_find $varname
-#
-# Find which file holds the effective last-assignment to a given variable
-# within the rc.conf(5) file(s).
-#
-# If the variable is found in any of the rc.conf(5) files, the function prints
-# the filename it was found in and then returns success. Otherwise output is
-# NULL and the function returns with error status.
-#
-f_sysrc_find()
-{
- local varname="${1%%[!$VALID_VARNAME_CHARS]*}"
- local regex="^[[:space:]]*$varname="
- local rc_conf_files="$( f_sysrc_get rc_conf_files )"
- local conf_files=
- local file
-
- # Check parameters
- case "$varname" in
- ""|[0-9]*) return $FAILURE
- esac
-
- #
- # If RC_CONFS is defined, set $rc_conf_files to an explicit
- # value, modifying the default behavior of source_rc_confs().
- #
- [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS"
-
- #
- # Reverse the order of files in rc_conf_files (the boot process sources
- # these in order, so we will search them in reverse-order to find the
- # last-assignment -- the one that ultimately effects the environment).
- #
- for file in $rc_conf_files; do
- conf_files="$file${conf_files:+ }$conf_files"
- done
-
- #
- # Append the defaults file (since directives in the defaults file
- # indeed affect the boot process, we'll want to know when a directive
- # is found there).
- #
- conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
-
- #
- # Find which file matches assignment to the given variable name.
- #
- for file in $conf_files; do
- [ -f "$file" -a -r "$file" ] || continue
- if grep -Eq "$regex" $file; then
- echo $file
- return $SUCCESS
- fi
- done
-
- return $FAILURE # Not found
-}
-
-# f_sysrc_desc $varname
-#
-# Attempts to return the comments associated with varname from the rc.conf(5)
-# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
-#
-# Multi-line comments are joined together. Results are NULL if no description
-# could be found.
-#
-# This function is a two-parter. Below is the awk(1) portion of the function,
-# afterward is the sh(1) function which utilizes the below awk script.
-#
-f_sysrc_desc_awk='
-# Variables that should be defined on the invocation line:
-# -v varname="varname"
-#
-BEGIN {
- regex = "^[[:space:]]*"varname"="
- found = 0
- buffer = ""
-}
-{
- if ( ! found )
- {
- if ( ! match($0, regex) ) next
-
- found = 1
- sub(/^[^#]*(#[[:space:]]*)?/, "")
- buffer = $0
- next
- }
-
- if ( !/^[[:space:]]*#/ ||
- /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
- /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
- /^[[:space:]]*$/ ) exit
-
- sub(/(.*#)*[[:space:]]*/, "")
- buffer = buffer" "$0
-}
-END {
- # Clean up the buffer
- sub(/^[[:space:]]*/, "", buffer)
- sub(/[[:space:]]*$/, "", buffer)
-
- print buffer
- exit ! found
-}
-'
-f_sysrc_desc()
-{
- awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
-}
-
-# f_sysrc_set $varname $new_value
-#
-# Change a setting in the system configuration files (edits the files in-place
-# to change the value in the last assignment to the variable). If the variable
-# does not appear in the source file, it is appended to the end of the primary
-# system configuration file `/etc/rc.conf'.
-#
-# This function is a two-parter. Below is the awk(1) portion of the function,
-# afterward is the sh(1) function which utilizes the below awk script.
-#
-f_sysrc_set_awk='
-# Variables that should be defined on the invocation line:
-# -v varname="varname"
-# -v new_value="new_value"
-#
-BEGIN {
- regex = "^[[:space:]]*"varname"="
- found = retval = 0
-}
-{
- # If already found... just spew
- if ( found ) { print; next }
-
- # Does this line match an assignment to our variable?
- if ( ! match($0, regex) ) { print; next }
-
- # Save important match information
- found = 1
- matchlen = RSTART + RLENGTH - 1
-
- # Store the value text for later munging
- value = substr($0, matchlen + 1, length($0) - matchlen)
-
- # Store the first character of the value
- t1 = t2 = substr(value, 0, 1)
-
- # Assignment w/ back-ticks, expression, or misc.
- # We ignore these since we did not generate them
- #
- if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
-
- # Assignment w/ single-quoted value
- else if ( t1 == "'\''" ) {
- sub(/^'\''[^'\'']*/, "", value)
- if ( length(value) == 0 ) t2 = ""
- sub(/^'\''/, "", value)
- }
-
- # Assignment w/ double-quoted value
- else if ( t1 == "\"" ) {
- sub(/^"(.*\\\\+")*[^"]*/, "", value)
- if ( length(value) == 0 ) t2 = ""
- sub(/^"/, "", value)
- }
-
- # Assignment w/ non-quoted value
- else if ( t1 ~ /[^[:space:];]/ ) {
- t1 = t2 = "\""
- sub(/^[^[:space:]]*/, "", value)
- }
-
- # Null-assignment
- else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
-
- printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
- t1, new_value, t2, value
-}
-END { exit retval }
-'
-f_sysrc_set()
-{
- local funcname=f_sysrc_set
- local varname="$1" new_value="$2"
-
- # Check arguments
- [ "$varname" ] || return $FAILURE
-
- #
- # Find which rc.conf(5) file contains the last-assignment
- #
- local not_found=
- local file="$( f_sysrc_find "$varname" )"
- if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
- #
- # We either got a null response (not found) or the variable
- # was only found in the rc.conf(5) defaults. In either case,
- # let's instead modify the first file from $rc_conf_files.
- #
-
- not_found=1
-
- #
- # If RC_CONFS is defined, use $RC_CONFS
- # rather than $rc_conf_files.
- #
- if [ "${RC_CONFS+set}" ]; then
- file="${RC_CONFS%%[$IFS]*}"
- else
- file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
- fi
- fi
-
- #
- # If not found, append new value to first file and return.
- #
- if [ "$not_found" ]; then
- # Add a newline if missing before appending to the file
- [ ! -e "$file" ] || awk 'BEGIN { wc = 0 } NR == 1 {
- (cmd = "wc -l " FILENAME) | getline
- close(cmd)
- wc = $1
- } END { exit wc != NR }' "$file" ||
- echo >> "$file" || return $?
- echo "$varname=\"$new_value\"" >> "$file"
- return $?
- fi
-
- #
- # Perform sanity checks.
- #
- if [ ! -w "$file" ]; then
- f_err "$msg_cannot_create_permission_denied\n" \
- "$pgm" "$file"
- return $FAILURE
- fi
-
- #
- # Create a new temporary file to write to.
- #
- local tmpfile
- if ! f_eval_catch -dk tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm"
- then
- echo "$tmpfile" >&2
- return $FAILURE
- fi
-
- #
- # Fixup permissions (else we're in for a surprise, as mktemp(1) creates
- # the temporary file with 0600 permissions, and if we simply mv(1) the
- # temporary file over the destination, the destination will inherit the
- # permissions from the temporary file).
- #
- local mode
- f_eval_catch -dk mode $funcname stat 'stat -f "%%#Lp" "%s"' "$file" ||
- mode=0644
- f_eval_catch -d $funcname chmod 'chmod "%s" "%s"' "$mode" "$tmpfile"
-
- #
- # Fixup ownership. The destination file _is_ writable (we tested
- # earlier above). However, this will fail if we don't have sufficient
- # permissions (so we throw stderr into the bit-bucket).
- #
- local owner
- f_eval_catch -dk owner $funcname stat \
- 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
- f_eval_catch -d $funcname chown 'chown "%s" "%s"' "$owner" "$tmpfile"
-
- #
- # Operate on the matching file, replacing only the last occurrence.
- #
- # Use awk to ensure LF at end of each line, else files without ending
- # LF will trigger a bug in `tail -r' where last two lines are joined.
- #
- local new_contents retval
- new_contents=$( awk 1 "$file" 2> /dev/null | tail -r )
- new_contents=$( echo "$new_contents" | awk -v varname="$varname" \
- -v new_value="$new_value" "$f_sysrc_set_awk" )
- retval=$?
-
- #
- # Write the temporary file contents.
- #
- echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE
- if [ $retval -ne $SUCCESS ]; then
- echo "$varname=\"$new_value\"" >> "$tmpfile"
- fi
-
- #
- # Taint-check our results.
- #
- if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"; then
- f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
- rm -f "$tmpfile"
- return $FAILURE
- fi
-
- #
- # Finally, move the temporary file into place.
- #
- f_eval_catch -de $funcname mv 'mv "%s" "%s"' "$tmpfile" "$file"
-}
-
-# f_sysrc_delete $varname
-#
-# Remove a setting from the system configuration files (edits files in-place).
-# Deletes all assignments to the given variable in all config files. If the
-# `-f file' option is passed, the removal is restricted to only those files
-# specified, otherwise the system collection of rc_conf_files is used.
-#
-# This function is a two-parter. Below is the awk(1) portion of the function,
-# afterward is the sh(1) function which utilizes the below awk script.
-#
-f_sysrc_delete_awk='
-# Variables that should be defined on the invocation line:
-# -v varname="varname"
-#
-BEGIN {
- regex = "^[[:space:]]*"varname"="
- found = 0
-}
-{
- if ( $0 ~ regex )
- found = 1
- else
- print
-}
-END { exit ! found }
-'
-f_sysrc_delete()
-{
- local funcname=f_sysrc_delete
- local varname="$1"
- local file
-
- # Check arguments
- [ "$varname" ] || return $FAILURE
-
- #
- # Operate on each of the specified files
- #
- local tmpfile
- for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do
- [ -e "$file" ] || continue
-
- #
- # Create a new temporary file to write to.
- #
- if ! f_eval_catch -dk tmpfile $funcname mktemp \
- 'mktemp -t "%s"' "$pgm"
- then
- echo "$tmpfile" >&2
- return $FAILURE
- fi
-
- #
- # Fixup permissions and ownership (mktemp(1) defaults to 0600
- # permissions) to instead match the destination file.
- #
- local mode owner
- f_eval_catch -dk mode $funcname stat \
- 'stat -f "%%#Lp" "%s"' "$file" || mode=0644
- f_eval_catch -dk owner $funcname stat \
- 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
- f_eval_catch -d $funcname chmod \
- 'chmod "%s" "%s"' "$mode" "$tmpfile"
- f_eval_catch -d $funcname chown \
- 'chown "%s" "%s"' "$owner" "$tmpfile"
-
- #
- # Operate on the file, removing all occurrences, saving the
- # output in our temporary file.
- #
- awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
- > "$tmpfile"
- if [ $? -ne $SUCCESS ]; then
- # The file didn't contain any assignments
- rm -f "$tmpfile"
- continue
- fi
-
- #
- # Taint-check our results.
- #
- if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"
- then
- f_err "$msg_previous_syntax_errors\n" \
- "$pgm" "$file"
- rm -f "$tmpfile"
- return $FAILURE
- fi
-
- #
- # Perform sanity checks
- #
- if [ ! -w "$file" ]; then
- f_err "$msg_permission_denied\n" "$pgm" "$file"
- rm -f "$tmpfile"
- return $FAILURE
- fi
-
- #
- # Finally, move the temporary file into place.
- #
- f_eval_catch -de $funcname mv \
- 'mv "%s" "%s"' "$tmpfile" "$file" || return $FAILURE
- done
-}
-
############################################################ MAIN
f_dprintf "%s: Successfully loaded." sysrc.subr
diff --git a/usr.sbin/sysrc/Makefile b/usr.sbin/sysrc/Makefile
index bad50caf2ffc..b0ddedab8fad 100644
--- a/usr.sbin/sysrc/Makefile
+++ b/usr.sbin/sysrc/Makefile
@@ -1,8 +1,11 @@
.include <src.opts.mk>
-PACKAGE= bsdconfig
+PACKAGE= rc
SCRIPTS= sysrc
+FILESDIR= ${SHAREDIR}/sysrc
+FILES= sysrc.subr
+
MAN= sysrc.8
HAS_TESTS=
diff --git a/usr.sbin/sysrc/sysrc b/usr.sbin/sysrc/sysrc
index 625ff5ca4efd..bd3faeac8ed3 100644
--- a/usr.sbin/sysrc/sysrc
+++ b/usr.sbin/sysrc/sysrc
@@ -27,12 +27,8 @@
#
############################################################ INCLUDES
-# Prevent `-d' from being interpreted as a debug flag by common.subr
-DEBUG_SELF_INITIALIZE=
-
-BSDCFG_SHARE="/usr/share/bsdconfig"
-[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
-[ "$_SYSRC_SUBR" ] || f_include $BSDCFG_SHARE/sysrc.subr
+SYSRC_SHARE="/usr/share/sysrc"
+[ "$_SYSRC_SUBR" ] || . $SYSRC_SHARE/sysrc.subr || exit 1
############################################################ GLOBALS
@@ -207,9 +203,7 @@ jail_depend()
#
# Print include dependencies
#
- echo DEBUG_SELF_INITIALIZE=
- cat $BSDCFG_SHARE/common.subr
- cat $BSDCFG_SHARE/sysrc.subr
+ cat $SYSRC_SHARE/sysrc.subr
}
# escape $string [$var_to_set]
diff --git a/usr.sbin/sysrc/sysrc.subr b/usr.sbin/sysrc/sysrc.subr
new file mode 100644
index 000000000000..63c92843c672
--- /dev/null
+++ b/usr.sbin/sysrc/sysrc.subr
@@ -0,0 +1,898 @@
+if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
+#
+# Copyright (c) 2006-2015 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# This file was split-out of bsdconfig(8) and is now self-contained, and is
+# no longer dependent on bsdconfig(8) or any of its libraries.
+#
+############################################################ CONFIGURATION
+
+#
+# Standard pathnames (inherit values from shell if available)
+#
+: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
+
+############################################################ GLOBALS
+
+#
+# Global exit status variables
+#
+SUCCESS=0
+FAILURE=1
+
+#
+# Program name (derived from the running script)
+#
+pgm="${0##*/}"
+
+#
+# Error messages.
+#
+msg_cannot_create_permission_denied="%s: cannot create %s: Permission denied"
+msg_permission_denied="%s: %s: Permission denied"
+msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax errors"
+
+#
+# Valid characters that can appear in an sh(1) variable name
+#
+# Please note that the character ranges A-Z and a-z should be avoided because
+# these can include accent characters (which are not valid in a variable name).
+# For example, A-Z matches any character that sorts after A but before Z,
+# including A and Z. Although ASCII order would make more sense, that is not
+# how it works.
+#
+VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
+
+############################################################ FUNCTIONS
+
+# f_err $format [$arguments ...]
+#
+# Print a message to stderr (fd=2).
+#
+f_err()
+{
+ printf "$@" >&2
+}
+
+# f_have $anything ...
+#
+# A wrapper to the `type' built-in. Returns true if argument is a valid shell
+# built-in, keyword, or externally-tracked binary, otherwise false.
+#
+f_have()
+{
+ type "$@" > /dev/null 2>&1
+}
+
+# setvar $var_to_set [$value]
+#
+# Implement setvar for shells unlike FreeBSD sh(1).
+#
+if ! f_have setvar; then
+setvar()
+{
+ [ $# -gt 0 ] || return $SUCCESS
+ local __setvar_var_to_set="$1" __setvar_right="$2" __setvar_left=
+ case $# in
+ 1) unset "$__setvar_var_to_set"
+ return $? ;;
+ 2) : fall through ;;
+ *) f_err "setvar: too many arguments\n"
+ return $FAILURE
+ esac
+ case "$__setvar_var_to_set" in *[!0-9A-Za-z_]*)
+ f_err "setvar: %s: bad variable name\n" "$__setvar_var_to_set"
+ return 2
+ esac
+ while case "$__setvar_r" in *\'*) : ;; *) false ; esac
+ do
+ __setvar_left="$__setvar_left${__setvar_right%%\'*}'\\''"
+ __setvar_right="${__setvar_right#*\'}"
+ done
+ __setvar_left="$__setvar_left${__setvar_right#*\'}"
+ eval "$__setvar_var_to_set='$__setvar_left'"
+}
+fi
+
+# f_getvar $var_to_get [$var_to_set]
+#
+# Utility function designed to go along with the already-builtin setvar.
+# Allows clean variable name indirection without forking or sub-shells.
+#
+# Returns error status if the requested variable ($var_to_get) is not set.
+#
+# If $var_to_set is missing or NULL, the value of $var_to_get is printed to
+# standard output for capturing in a sub-shell.
+#
+f_getvar()
+{
+ local __var_to_get="$1" __var_to_set="$2"
+ [ "$__var_to_set" ] || local value
+ eval [ \"\${$__var_to_get+set}\" ]
+ local __retval=$?
+ eval ${__var_to_set:-value}=\"\${$__var_to_get}\"
+ [ "$__var_to_set" ] || { [ "$value" ] && echo "$value"; }
+ return $__retval
+}
+
+# f_eval_catch [ -d ] [ -e ] [ -k $var_to_set ] $funcname $utility \
+# $printf_format [$printf_args ...]
+#
+# Evaluate the printf-rendered command line and capture its output. The return
+# status of the command is preserved. If `-k' is given, output is stored in
+# $var_to_set. If `-e' is given, output is echoed to stderr on failure.
+#
+f_eval_catch()
+{
+ local OPTIND OPTARG __flag __show_err= __var_to_set=
+ while getopts "dek:" __flag > /dev/null; do
+ case "$__flag" in
+ d) : # no-op: always non-interactive here
+ ;;
+ e) __show_err=1 ;;
+ k) __var_to_set="$OPTARG" ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ local __funcname="$1" __utility="$2"; shift 2
+ local __cmd __output __retval
+
+ __cmd=$( printf -- "$@" )
+ __output=$( exec 2>&1; eval "$__cmd" )
+ __retval=$?
+ if [ "$__output" -a "$__show_err" ]; then
+ echo "$__output" >&2
+ fi
+ [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output"
+ return $__retval
+}
+
+# source_rc_confs
+#
+# Source the rc.conf(5) override files (as defined in rc.conf(5)), preserving
+# the semantics used by /etc/rc.* at boot-time.
+#
+source_rc_confs()
+{
+ local i sourced_files
+ for i in ${rc_conf_files}; do
+ case ${sourced_files} in
+ *:$i:*)
+ ;;
+ *)
+ sourced_files="${sourced_files}:$i:"
+ if [ -r $i ]; then
+ . $i
+ fi
+ ;;
+ esac
+ done
+ # Re-do process to pick up [possibly] redefined $rc_conf_files
+ for i in ${rc_conf_files}; do
+ case ${sourced_files} in
+ *:$i:*)
+ ;;
+ *)
+ sourced_files="${sourced_files}:$i:"
+ if [ -r $i ]; then
+ . $i
+ fi
+ ;;
+ esac
+ done
+}
+
+
+# f_clean_env [ --except $varname ... ]
+#
+# Unset all environment variables in the current scope. An optional list of
+# arguments can be passed, indicating which variables to avoid unsetting; the
+# `--except' is required to enable the exclusion-list as the remainder of
+# positional arguments.
+#
+# Be careful not to call this in a shell that you still expect to perform
+# $PATH expansion in, because this will blow $PATH away. This is best used
+# within a sub-shell block "(...)" or "$(...)" or "`...`".
+#
+f_clean_env()
+{
+ local var arg except=
+
+ #
+ # Should we process an exclusion-list?
+ #
+ if [ "$1" = "--except" ]; then
+ except=1
+ shift 1
+ fi
+
+ #
+ # Loop over a list of variable names from set(1) built-in.
+ #
+ for var in $( set | awk -F= \
+ '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
+ | grep -v '^except$'
+ ); do
+ #
+ # In POSIX bourne-shell, attempting to unset(1) OPTIND results
+ # in "unset: Illegal number:" and causes abrupt termination.
+ #
+ [ "$var" = OPTIND ] && continue
+
+ #
+ # Process the exclusion-list?
+ #
+ if [ "$except" ]; then
+ for arg in "$@" ""; do
+ [ "$var" = "$arg" ] && break
+ done
+ [ "$arg" ] && continue
+ fi
+
+ unset "$var"
+ done
+}
+
+# f_sysrc_get $varname
+#
+# Get a system configuration setting from the collection of system-
+# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and
+# /etc/rc.conf.local)
+#
+# NOTE: Additional shell parameter-expansion formats are supported. For
+# example, passing an argument of "hostname%%.*" (properly quoted) will
+# return the hostname up to (but not including) the first `.' (see sh(1),
+# "Parameter Expansion" for more information on additional formats).
+#
+f_sysrc_get()
+{
+ # Sanity check
+ [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
+
+ # Taint-check variable name
+ case "$1" in
+ [0-9]*)
+ # Don't expand possible positional parameters
+ return $FAILURE ;;
+ *)
+ [ "$1" ] || return $FAILURE
+ esac
+
+ ( # Execute within sub-shell to protect parent environment
+
+ #
+ # Clear the environment of all variables, preventing the
+ # expansion of normals such as `PS1', `TERM', etc.
+ #
+ f_clean_env --except IFS RC_CONFS RC_DEFAULTS
+
+ . "$RC_DEFAULTS" > /dev/null 2>&1
+
+ unset RC_DEFAULTS
+ # no longer needed
+
+ #
+ # If the query is for `rc_conf_files' then store the value that
+ # we inherited from sourcing RC_DEFAULTS (above) so that we may
+ # conditionally restore this value after source_rc_confs in the
+ # event that RC_CONFS does not customize the value.
+ #
+ if [ "$1" = "rc_conf_files" ]; then
+ _rc_conf_files="$rc_conf_files"
+ fi
+
+ #
+ # If RC_CONFS is defined, set $rc_conf_files to an explicit
+ # value, modifying the default behavior of source_rc_confs().
+ #
+ if [ "${RC_CONFS+set}" ]; then
+ rc_conf_files="$RC_CONFS"
+ _rc_confs_set=1
+ fi
+
+ source_rc_confs > /dev/null 2>&1
+
+ #
+ # If the query was for `rc_conf_files' AND after calling
+ # source_rc_confs the value has not changed, then we should
+ # restore the value to the one inherited from RC_DEFAULTS
+ # before performing the final query (preventing us from
+ # returning what was set via RC_CONFS when the intent was
+ # instead to query the value from the file(s) specified).
+ #
+ if [ "$1" = "rc_conf_files" -a \
+ "$_rc_confs_set" -a \
+ "$rc_conf_files" = "$RC_CONFS" \
+ ]; then
+ rc_conf_files="$_rc_conf_files"
+ unset _rc_conf_files
+ unset _rc_confs_set
+ fi
+
+ unset RC_CONFS
+ # no longer needed
+
+ #
+ # This must be the last functional line for both the sub-shell
+ # and the function to preserve the return status from formats
+ # such as "${varname?}" and "${varname:?}" (see "Parameter
+ # Expansion" in sh(1) for more information).
+ #
+ eval printf "'%s\\n'" '"${'"$1"'}"' 2> /dev/null
+ )
+}
+
+# f_sysrc_service_configs [-a|-p] $name [$var_to_set]
+#
+# Get a list of optional `rc.conf.d' entries sourced by system `rc.d' script
+# $name (see rc.subr(8) for additional information on `rc.conf.d'). If $name
+# exists in `/etc/rc.d' or $local_startup directories and is an rc(8) script
+# the result is a space separated list of `rc.conf.d' entries sourced by the
+# $name `rc.d' script. Otherwise, if $name exists as a binary `rc.d' script,
+# the result is ``/etc/rc.conf.d/$name /usr/local/etc/rc.conf.d/$name''. The
+# result is NULL if $name does not exist.
+#
+# If $var_to_set is missing or NULL, output is to standard out. Returns success
+# if $name was found, failure otherwise.
+#
+# If `-a' flag is given and $var_to_set is non-NULL, append result to value of
+# $var_to_set rather than overwriting current contents.
+#
+# If `-p' flag is given and $var_to_set is non-NULL, prepend result to value of
+# $var_to_set rather than overwriting current contents.
+#
+# NB: The `-a' and `-p' option flags are mutually exclusive.
+#
+f_sysrc_service_configs()
+{
+ local OPTIND=1 OPTARG __flag __append= __prepend=
+ local __local_startup __dir __spath __stype __names=
+
+ while getopts ap __flag; do
+ case "$__flag" in
+ a) __append=1 __prepend= ;;
+ p) __prepend=1 __append= ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ [ $# -gt 0 ] || return $FAILURE
+ local __sname="$1" __var_to_set="$2"
+
+ __local_startup=$( f_sysrc_get local_startup )
+ for __dir in /etc/rc.d $__local_startup; do
+ __spath="$__dir/$__sname"
+ [ -f "$__spath" -a -x "$__spath" ] || __spath= continue
+ break
+ done
+ [ "$__spath" ] || return $FAILURE
+
+ __stype=$( file -b "$__spath" 2> /dev/null )
+ case "$__stype" in
+ *"shell script"*)
+ __names=$( exec 9<&1 1>&- 2>&-
+ last_name=
+ print_name() {
+ local name="$1"
+ case "$name" in
+ ""|.|..|*/*|"$last_name") return ;;
+ esac
+ echo "$name" >&9
+ last_name="$name"
+ }
+ eval "$( awk '{
+ gsub(/load_rc_config /, "print_name ")
+ gsub(/run_rc_command /, ": ")
+ print
+ }' "$__spath" )"
+ ) ;;
+ *)
+ __names="$__sname"
+ esac
+
+ local __name __test_path __configs=
+ for __name in $__names; do
+ for __dir in /etc/rc.d $__local_startup; do
+ __test_path="${__dir%/rc.d}/rc.conf.d/$__name"
+ [ -d "$__test_path" ] ||
+ __configs="$__configs $__test_path" continue
+ for __test_path in "$__test_path"/*; do
+ [ -f "$__test_path" ] || continue
+ __configs="$__configs $__test_path"
+ done
+ done
+ done
+ __configs="${__configs# }"
+
+ if [ "$__var_to_set" ]; then
+ local __cur=
+ [ "$__append" -o "$__prepend" ] &&
+ f_getvar "$__var_to_set" __cur
+ [ "$__append" ] && __configs="$__cur{$__cur:+ }$__configs"
+ [ "$__prepend" ] && __configs="$__configs${__cur:+ }$__cur"
+ setvar "$__var_to_set" "$__configs"
+ else
+ echo "$__configs"
+ fi
+
+ return $SUCCESS
+}
+
+# f_sysrc_get_default $varname
+#
+# Get a system configuration default setting from the default rc.conf(5) file
+# (or whatever RC_DEFAULTS points at).
+#
+f_sysrc_get_default()
+{
+ # Sanity check
+ [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
+
+ # Taint-check variable name
+ case "$1" in
+ [0-9]*)
+ # Don't expand possible positional parameters
+ return $FAILURE ;;
+ *)
+ [ "$1" ] || return $FAILURE
+ esac
+
+ ( # Execute within sub-shell to protect parent environment
+
+ #
+ # Clear the environment of all variables, preventing the
+ # expansion of normals such as `PS1', `TERM', etc.
+ #
+ f_clean_env --except RC_DEFAULTS
+
+ . "$RC_DEFAULTS" > /dev/null 2>&1
+
+ unset RC_DEFAULTS
+ # no longer needed
+
+ #
+ # This must be the last functional line for both the sub-shell
+ # and the function to preserve the return status from formats
+ # such as "${varname?}" and "${varname:?}" (see "Parameter
+ # Expansion" in sh(1) for more information).
+ #
+ eval printf "'%s\\n'" '"${'"$1"'}"' 2> /dev/null
+ )
+}
+
+# f_sysrc_find $varname
+#
+# Find which file holds the effective last-assignment to a given variable
+# within the rc.conf(5) file(s).
+#
+# If the variable is found in any of the rc.conf(5) files, the function prints
+# the filename it was found in and then returns success. Otherwise output is
+# NULL and the function returns with error status.
+#
+f_sysrc_find()
+{
+ local varname="${1%%[!$VALID_VARNAME_CHARS]*}"
+ local regex="^[[:space:]]*$varname="
+ local rc_conf_files="$( f_sysrc_get rc_conf_files )"
+ local conf_files=
+ local file
+
+ # Check parameters
+ case "$varname" in
+ ""|[0-9]*) return $FAILURE
+ esac
+
+ #
+ # If RC_CONFS is defined, set $rc_conf_files to an explicit
+ # value, modifying the default behavior of source_rc_confs().
+ #
+ [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS"
+
+ #
+ # Reverse the order of files in rc_conf_files (the boot process sources
+ # these in order, so we will search them in reverse-order to find the
+ # last-assignment -- the one that ultimately effects the environment).
+ #
+ for file in $rc_conf_files; do
+ conf_files="$file${conf_files:+ }$conf_files"
+ done
+
+ #
+ # Append the defaults file (since directives in the defaults file
+ # indeed affect the boot process, we'll want to know when a directive
+ # is found there).
+ #
+ conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
+
+ #
+ # Find which file matches assignment to the given variable name.
+ #
+ for file in $conf_files; do
+ [ -f "$file" -a -r "$file" ] || continue
+ if grep -Eq "$regex" $file; then
+ echo $file
+ return $SUCCESS
+ fi
+ done
+
+ return $FAILURE # Not found
+}
+
+# f_sysrc_desc $varname
+#
+# Attempts to return the comments associated with varname from the rc.conf(5)
+# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
+#
+# Multi-line comments are joined together. Results are NULL if no description
+# could be found.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_desc_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = 0
+ buffer = ""
+}
+{
+ if ( ! found )
+ {
+ if ( ! match($0, regex) ) next
+
+ found = 1
+ sub(/^[^#]*(#[[:space:]]*)?/, "")
+ buffer = $0
+ next
+ }
+
+ if ( !/^[[:space:]]*#/ ||
+ /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
+ /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
+ /^[[:space:]]*$/ ) exit
+
+ sub(/(.*#)*[[:space:]]*/, "")
+ buffer = buffer" "$0
+}
+END {
+ # Clean up the buffer
+ sub(/^[[:space:]]*/, "", buffer)
+ sub(/[[:space:]]*$/, "", buffer)
+
+ print buffer
+ exit ! found
+}
+'
+f_sysrc_desc()
+{
+ awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
+}
+
+# f_sysrc_set $varname $new_value
+#
+# Change a setting in the system configuration files (edits the files in-place
+# to change the value in the last assignment to the variable). If the variable
+# does not appear in the source file, it is appended to the end of the primary
+# system configuration file `/etc/rc.conf'.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_set_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+# -v new_value="new_value"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = retval = 0
+}
+{
+ # If already found... just spew
+ if ( found ) { print; next }
+
+ # Does this line match an assignment to our variable?
+ if ( ! match($0, regex) ) { print; next }
+
+ # Save important match information
+ found = 1
+ matchlen = RSTART + RLENGTH - 1
+
+ # Store the value text for later munging
+ value = substr($0, matchlen + 1, length($0) - matchlen)
+
+ # Store the first character of the value
+ t1 = t2 = substr(value, 0, 1)
+
+ # Assignment w/ back-ticks, expression, or misc.
+ # We ignore these since we did not generate them
+ #
+ if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
+
+ # Assignment w/ single-quoted value
+ else if ( t1 == "'\''" ) {
+ sub(/^'\''[^'\'']*/, "", value)
+ if ( length(value) == 0 ) t2 = ""
+ sub(/^'\''/, "", value)
+ }
+
+ # Assignment w/ double-quoted value
+ else if ( t1 == "\"" ) {
+ sub(/^"(.*\\\\+")*[^"]*/, "", value)
+ if ( length(value) == 0 ) t2 = ""
+ sub(/^"/, "", value)
+ }
+
+ # Assignment w/ non-quoted value
+ else if ( t1 ~ /[^[:space:];]/ ) {
+ t1 = t2 = "\""
+ sub(/^[^[:space:]]*/, "", value)
+ }
+
+ # Null-assignment
+ else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
+
+ printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
+ t1, new_value, t2, value
+}
+END { exit retval }
+'
+f_sysrc_set()
+{
+ local funcname=f_sysrc_set
+ local varname="$1" new_value="$2"
+
+ # Check arguments
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # Find which rc.conf(5) file contains the last-assignment
+ #
+ local not_found=
+ local file="$( f_sysrc_find "$varname" )"
+ if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
+ #
+ # We either got a null response (not found) or the variable
+ # was only found in the rc.conf(5) defaults. In either case,
+ # let's instead modify the first file from $rc_conf_files.
+ #
+
+ not_found=1
+
+ #
+ # If RC_CONFS is defined, use $RC_CONFS
+ # rather than $rc_conf_files.
+ #
+ if [ "${RC_CONFS+set}" ]; then
+ file="${RC_CONFS%%[$IFS]*}"
+ else
+ file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
+ fi
+ fi
+
+ #
+ # If not found, append new value to first file and return.
+ #
+ if [ "$not_found" ]; then
+ # Add a newline if missing before appending to the file
+ [ ! -e "$file" ] || awk 'BEGIN { wc = 0 } NR == 1 {
+ (cmd = "wc -l " FILENAME) | getline
+ close(cmd)
+ wc = $1
+ } END { exit wc != NR }' "$file" ||
+ echo >> "$file" || return $?
+ echo "$varname=\"$new_value\"" >> "$file"
+ return $?
+ fi
+
+ #
+ # Perform sanity checks.
+ #
+ if [ ! -w "$file" ]; then
+ f_err "$msg_cannot_create_permission_denied\n" \
+ "$pgm" "$file"
+ return $FAILURE
+ fi
+
+ #
+ # Create a new temporary file to write to.
+ #
+ local tmpfile
+ if ! f_eval_catch -dk tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm"
+ then
+ echo "$tmpfile" >&2
+ return $FAILURE
+ fi
+
+ #
+ # Fixup permissions (else we're in for a surprise, as mktemp(1) creates
+ # the temporary file with 0600 permissions, and if we simply mv(1) the
+ # temporary file over the destination, the destination will inherit the
+ # permissions from the temporary file).
+ #
+ local mode
+ f_eval_catch -dk mode $funcname stat 'stat -f "%%#Lp" "%s"' "$file" ||
+ mode=0644
+ f_eval_catch -d $funcname chmod 'chmod "%s" "%s"' "$mode" "$tmpfile"
+
+ #
+ # Fixup ownership. The destination file _is_ writable (we tested
+ # earlier above). However, this will fail if we don't have sufficient
+ # permissions (so we throw stderr into the bit-bucket).
+ #
+ local owner
+ f_eval_catch -dk owner $funcname stat \
+ 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
+ f_eval_catch -d $funcname chown 'chown "%s" "%s"' "$owner" "$tmpfile"
+
+ #
+ # Operate on the matching file, replacing only the last occurrence.
+ #
+ # Use awk to ensure LF at end of each line, else files without ending
+ # LF will trigger a bug in `tail -r' where last two lines are joined.
+ #
+ local new_contents retval
+ new_contents=$( awk 1 "$file" 2> /dev/null | tail -r )
+ new_contents=$( echo "$new_contents" | awk -v varname="$varname" \
+ -v new_value="$new_value" "$f_sysrc_set_awk" )
+ retval=$?
+
+ #
+ # Write the temporary file contents.
+ #
+ echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE
+ if [ $retval -ne $SUCCESS ]; then
+ echo "$varname=\"$new_value\"" >> "$tmpfile"
+ fi
+
+ #
+ # Taint-check our results.
+ #
+ if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"; then
+ f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Finally, move the temporary file into place.
+ #
+ f_eval_catch -de $funcname mv 'mv "%s" "%s"' "$tmpfile" "$file"
+}
+
+# f_sysrc_delete $varname
+#
+# Remove a setting from the system configuration files (edits files in-place).
+# Deletes all assignments to the given variable in all config files. If the
+# `-f file' option is passed, the removal is restricted to only those files
+# specified, otherwise the system collection of rc_conf_files is used.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_delete_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = 0
+}
+{
+ if ( $0 ~ regex )
+ found = 1
+ else
+ print
+}
+END { exit ! found }
+'
+f_sysrc_delete()
+{
+ local funcname=f_sysrc_delete
+ local varname="$1"
+ local file
+
+ # Check arguments
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # Operate on each of the specified files
+ #
+ local tmpfile
+ for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do
+ [ -e "$file" ] || continue
+
+ #
+ # Create a new temporary file to write to.
+ #
+ if ! f_eval_catch -dk tmpfile $funcname mktemp \
+ 'mktemp -t "%s"' "$pgm"
+ then
+ echo "$tmpfile" >&2
+ return $FAILURE
+ fi
+
+ #
+ # Fixup permissions and ownership (mktemp(1) defaults to 0600
+ # permissions) to instead match the destination file.
+ #
+ local mode owner
+ f_eval_catch -dk mode $funcname stat \
+ 'stat -f "%%#Lp" "%s"' "$file" || mode=0644
+ f_eval_catch -dk owner $funcname stat \
+ 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
+ f_eval_catch -d $funcname chmod \
+ 'chmod "%s" "%s"' "$mode" "$tmpfile"
+ f_eval_catch -d $funcname chown \
+ 'chown "%s" "%s"' "$owner" "$tmpfile"
+
+ #
+ # Operate on the file, removing all occurrences, saving the
+ # output in our temporary file.
+ #
+ awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
+ > "$tmpfile"
+ if [ $? -ne $SUCCESS ]; then
+ # The file didn't contain any assignments
+ rm -f "$tmpfile"
+ continue
+ fi
+
+ #
+ # Taint-check our results.
+ #
+ if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"
+ then
+ f_err "$msg_previous_syntax_errors\n" \
+ "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Perform sanity checks
+ #
+ if [ ! -w "$file" ]; then
+ f_err "$msg_permission_denied\n" "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Finally, move the temporary file into place.
+ #
+ f_eval_catch -de $funcname mv \
+ 'mv "%s" "%s"' "$tmpfile" "$file" || return $FAILURE
+ done
+}
+
+############################################################ MAIN
+
+fi # ! $_SYSRC_SUBR