mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 03:18:02 +01:00
sysrc: make independant from bsdconfig(8)
MFC After: 1 week Approved by: dteske Reviewed by: dteske Differential Revision: https://reviews.freebsd.org/D59658
This commit is contained in:
1 parent
42c69445ca
commit
eaeb5f29bc
7 files changed
+916
-728
No files matched your search
+1
-1
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user