diff options
Diffstat (limited to 'bsdconfig/startup/share')
-rw-r--r-- | bsdconfig/startup/share/Makefile | 6 | ||||
-rw-r--r-- | bsdconfig/startup/share/Makefile.depend | 11 | ||||
-rw-r--r-- | bsdconfig/startup/share/rcconf.subr | 500 | ||||
-rw-r--r-- | bsdconfig/startup/share/rcedit.subr | 90 | ||||
-rw-r--r-- | bsdconfig/startup/share/rcvar.subr | 236 |
5 files changed, 843 insertions, 0 deletions
diff --git a/bsdconfig/startup/share/Makefile b/bsdconfig/startup/share/Makefile new file mode 100644 index 000000000000..134914cb0880 --- /dev/null +++ b/bsdconfig/startup/share/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +FILESDIR= ${SHAREDIR}/bsdconfig/startup +FILES= rcconf.subr rcedit.subr rcvar.subr + +.include <bsd.prog.mk> diff --git a/bsdconfig/startup/share/Makefile.depend b/bsdconfig/startup/share/Makefile.depend new file mode 100644 index 000000000000..f80275d86ab1 --- /dev/null +++ b/bsdconfig/startup/share/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bsdconfig/startup/share/rcconf.subr b/bsdconfig/startup/share/rcconf.subr new file mode 100644 index 000000000000..80b6504994cc --- /dev/null +++ b/bsdconfig/startup/share/rcconf.subr @@ -0,0 +1,500 @@ +if [ ! "$_STARTUP_RCCONF_SUBR" ]; then _STARTUP_RCCONF_SUBR=1 +# +# Copyright (c) 2006-2013 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." startup/rcconf.subr +f_include $BSDCFG_SHARE/sysrc.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCCONF_MAP= +_STARTUP_RCCONF_MAP= + +# +# Define what a variable looks like +# +STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcconf_list +# +# Produce a list of non-default configuration variables configured in the +# rc.conf(5) collection of files. +# +f_startup_rcconf_list() +{ + ( # Operate within a sub-shell to protect the parent environment + . "$RC_DEFAULTS" > /dev/null + f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files + source_rc_confs > /dev/null + export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" + export RC_DEFAULTS + set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"_rc_conf_files_file\" ) return + if ( var == \"rc_conf_files\" ) + { + if ( ENVIRON[\"_rc_conf_files_file\"] == \ + ENVIRON[\"RC_DEFAULTS\"] ) return + } + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" + ) +} + +# f_startup_rcconf_map [$var_to_set] +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) +# variables and their descriptions. The map returned has the following format: +# +# var description +# +# With each as follows: +# +# var the rc.conf(5) variable +# description description of the variable +# +# If $var_to_set is missing or NULL, the map is printed to standard output for +# capturing in a sub-shell (which is less-recommended because of performance +# degredation; for example, when called in a loop). +# +f_startup_rcconf_map() +{ + local __funcname=f_startup_rcconf_map + local __var_to_set="$1" + + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCCONF_MAP" ]; then + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" + else + echo "$STARTUP_RCCONF_MAP" + fi + return $SUCCESS + fi + + # + # Create the in-memory cache (potentially from validated on-disk cache) + # + + # + # Calculate digest used to determine if the on-disk global persistent + # cache file (containing this digest on the first line) is valid and + # can be used to quickly populate the cache value for immediate return. + # + local __rc_defaults_digest + __rc_defaults_digest=$( exec 2> /dev/null; md5 < "$RC_DEFAULTS" ) + + # + # Check to see if the global persistent cache file exists + # + if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # validated on-disk cache. If validation fails, fall-back to + # the current value and provide error exit status. + # + STARTUP_RCCONF_MAP=$( + ( # Get digest as the first word on first line + read digest rest_ignored + + # + # If the stored digest matches the calculated- + # one populate the in-memory cache from the on- + # disk cache and provide success exit status. + # + if [ "$digest" = "$__rc_defaults_digest" ] + then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCCONF_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCCONF_MAP_CACHEFILE" + ) + local __retval=$? + export STARTUP_RCCONF_MAP # Make children faster (export cache) + if [ $__retval -eq $SUCCESS ]; then + export _STARTUP_RCCONF_MAP=1 + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" + else + echo "$STARTUP_RCCONF_MAP" + fi + return $SUCCESS + fi + # Otherwise, fall-thru to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistent + # cache file to speed up future executions). + # + + STARTUP_RCCONF_MAP=$( + f_clean_env --except \ + PATH \ + RC_DEFAULTS \ + STARTUP_RCCONF_REGEX \ + f_sysrc_desc_awk + . "$RC_DEFAULTS" + + # Unset variables we don't want reported + unset source_rc_confs_defined + + for var in $( set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"f_sysrc_desc_awk\" ) return + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } + " ); do + echo $var "$( f_sysrc_desc $var )" + done + ) + export STARTUP_RCCONF_MAP + export _STARTUP_RCCONF_MAP=1 + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" + else + echo "$STARTUP_RCCONF_MAP" + fi + + # + # Attempt to create the persistent global cache + # + + # Create a new temporary file to write to + local __tmpfile + f_eval_catch -dk __tmpfile $__funcname mktemp \ + 'mktemp -t "%s"' "$pgm" || return $FAILURE + + # Write the temporary file contents + echo "$__rc_defaults_digest" > "$__tmpfile" + echo "$STARTUP_RCCONF_MAP" >> "$__tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCCONF_MAP_CACHEFILE" in + */*) f_eval_catch -d $__funcname mkdir \ + 'mkdir -p "%s"' "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" + esac + f_eval_catch -d $__funcname mv \ + 'mv "%s" "%s"' "$__tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" +} + +# f_startup_rcconf_map_expand $var_to_get +# +# Expands the map ($var_to_get) into the shell environment namespace by +# creating _${var}_desc variables containing the description of each variable +# encountered. +# +# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. +# +f_startup_rcconf_map_expand() +{ + local var_to_get="$1" + eval "$( debug= f_getvar "$var_to_get" | awk ' + BEGIN { + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + var = $1 + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf "_%s_desc='\''%s'\''\n", var, desc + printf "export _%s_desc\n", var + }' )" +} + +# f_dialog_input_view_details +# +# Display a menu for selecting which details are to be displayed. The following +# variables are tracked/modified by the menu/user's selection: +# +# SHOW_DESC Show or hide descriptions +# +# Mutually exclusive options: +# +# SHOW_VALUE Show the value (default; override only) +# SHOW_DEFAULT_VALUE Show both value and default +# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in +# +# Each variable is treated as a boolean (NULL for false, non-NULL for true). +# +# Variables are exported for later-required awk(1) ENVIRON visibility. Returns +# success unless the user chose `Cancel' or pressed Escape. +# +f_dialog_input_view_details() +{ + local prompt= + local menu_list # calculated below + local defaultitem= # calculated below + local hline="$hline_arrows_tab_enter" + + # Calculate marks for checkboxes and radio buttons + local md=" " + if [ "$SHOW_DESC" ]; then + md="X" + fi + local m1=" " m2=" " m3=" " + if [ "$SHOW_VALUE" ]; then + m1="*" + defaultitem="1 ($m1) $msg_show_value" + elif [ "$SHOW_DEFAULT_VALUE" ]; then + m2="*" + defaultitem="2 ($m2) $msg_show_default_value" + elif [ "$SHOW_CONFIGURED" ]; then + m3="*" + defaultitem="3 ($m3) $msg_show_configured" + fi + + # Create the menu list with the above-calculated marks + menu_list=" + 'R $msg_reset' '$msg_reset_desc' + 'D [$md] $msg_desc' '$msg_desc_desc' + '1 ($m1) $msg_show_value' '$msg_show_value_desc' + '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' + '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' + " # END-QUOTE + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + f_dialog_title "$msg_choose_view_details" + + local mtag + mtag=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize mtag + + f_dialog_title_restore + + [ $retval -eq $DIALOG_OK ] || return $DIALOG_CANCEL + + case "$mtag" in + "R $msg_reset") + SHOW_VALUE=1 + SHOW_DESC=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "D [X] $msg_desc") SHOW_DESC= ;; + "D [ ] $msg_desc") SHOW_DESC=1 ;; + "1 ("?") $msg_show_value") + SHOW_VALUE=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "2 ("?") $msg_show_default_value") + SHOW_VALUE= + SHOW_DEFAULT_VALUE=1 + SHOW_CONFIGURED= + ;; + "3 ("?") $msg_show_configured") + SHOW_VALUE= + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED=1 + ;; + esac +} + +# f_dialog_input_rclist [$default] +# +# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This +# function should be treated like a call to dialog(1) (the exit status should +# be captured and f_dialog_menutag_fetch() should be used to get the user's +# response). Optionally if present and non-null, highlight $default rcvar. +# +f_dialog_input_rclist() +{ + local prompt="$msg_please_select_an_rcconf_directive" + local menu_list=" + 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_this_menu'} + " # END-QUOTE + local defaultitem="$1" + local hline="$hline_arrows_tab_enter" + + if [ ! "$_RCCONF_MAP" ]; then + # Generate RCCONF_MAP of `var desc ...' per-line + f_dialog_info "$msg_creating_rcconf_map" + RCCONF_MAP=$( f_startup_rcconf_map ) + export RCCONF_MAP + # Generate _${var}_desc variables from $RCCONF_MAP + f_startup_rcconf_map_expand + export _RCCONF_MAP=1 + fi + + menu_list="$menu_list $( + export SHOW_DESC + echo "$RCCONF_MAP" | awk ' + BEGIN { + prefix = "" + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + rcvar = $1 + printf "%s'\'' '\'\''", rcvar + if ( ENVIRON["SHOW_DESC"] ) { + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # set noglob because descriptions in the $menu_list may contain + # `*' and get expanded by dialog(1) (doesn't affect Xdialog(1)). + # This prevents dialog(1) from expanding wildcards in help line. + + local height width rows + eval f_dialog_menu${SHOW_DESC:+_with_help}_size \ + height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --default-item \"\$defaultitem\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_menutag_store -s "$menu_choice" + return $retval +} + +# f_dialog_input_rcvar [$init] +# +# Allows the user to enter the name for a new rc.conf(5) variable. If the user +# does not cancel or press ESC, the $rcvar variable will hold the newly- +# configured value upon return. +# +f_dialog_input_rcvar() +{ + # + # Loop until the user provides taint-free/valid input + # + local _input="$1" + while :; do + + # Return if user either pressed ESC or chosen Cancel/No + f_dialog_input _input "$msg_please_enter_rcvar_name" \ + "$_input" "$hline_alnum_tab_enter" || return $? + + # Check for invalid entry (1of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then + f_show_msg "$msg_rcvar_must_start_with" + continue + fi + + # Check for invalid entry (2of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" + then + f_show_msg "$msg_rcvar_contains_invalid_chars" + continue + fi + + rcvar="$_input" + break + done + + f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" + + return $DIALOG_OK +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." startup/rcconf.subr + +fi # ! $_STARTUP_RCCONF_SUBR diff --git a/bsdconfig/startup/share/rcedit.subr b/bsdconfig/startup/share/rcedit.subr new file mode 100644 index 000000000000..1adca477ebf4 --- /dev/null +++ b/bsdconfig/startup/share/rcedit.subr @@ -0,0 +1,90 @@ +if [ ! "$_STARTUP_RCEDIT_SUBR" ]; then _STARTUP_RCEDIT_SUBR=1 +# +# Copyright (c) 2012 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." startup/rcedit.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/sysrc.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_rcedit $var [[--] $init ...] +# +# Allow the user to enter a new value for a given rc.conf(5) variable. If the +# user does not cancel or press ESC, the variable will be saved without +# confirmation. +# +# If the second argument is non-NULL, it will be processed as the initial text +# to be displayed, overriding the default behavior to display the currently +# configured value as the initial text. +# +# If instead the second argument is "--", then the third argument (NULL or +# otherwise) will be treated as the initial text. +# +f_dialog_rcedit() +{ + local funcname=f_dialog_rcedit + local msg var="$1" _input + + f_sprintf msg "$msg_please_enter_a_new_value" \ + "$var" "$( f_sysrc_get_default "$var" )" + + shift 1 # var + if [ "$1" ]; then + [ "$1" = "--" ] && shift 1 # -- + _input="$1" + else + _input=$( f_sysrc_get "$var" ) + fi + + # Return if user has either pressed ESC or chosen Cancel/No + f_dialog_input _input "$msg" "$_input" \ + "$hline_alnum_punc_tab_enter" || return $? + + # Return if the value has not changed from current + local cur_val="$( f_sysrc_get "$var" )" + [ "$_input" = "$cur_val" ] && return $DIALOG_OK + + f_dprintf "%s: [%s]->[%s]" "$var" "$cur_val" "$_input" + + f_eval_catch $funcname f_sysrc_set \ + 'f_sysrc_set "%s" "%s"' "$var" "$_input" +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." startup/rcedit.subr + +fi # ! $_STARTUP_RCEDIT_SUBR diff --git a/bsdconfig/startup/share/rcvar.subr b/bsdconfig/startup/share/rcvar.subr new file mode 100644 index 000000000000..c1a6ff2443e2 --- /dev/null +++ b/bsdconfig/startup/share/rcvar.subr @@ -0,0 +1,236 @@ +if [ ! "$_STARTUP_RCVAR_SUBR" ]; then _STARTUP_RCVAR_SUBR=1 +# +# Copyright (c) 2006-2013 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." startup/rcvar.subr +f_include $BSDCFG_SHARE/sysrc.subr + +############################################################ CONFIGURATION + +# +# Default path to the `/etc/rc.d' directory where service(8) scripts are stored +# +: ${ETC_RC_D:=/etc/rc.d} + +# +# Default path to `/etc/rc.subr' (for find_local_scripts_new()) +# +: ${ETC_RC_SUBR:=/etc/rc.subr} + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCVAR_MAP= +_STARTUP_RCVAR_MAP= + +# +# Define what an rcvar looks like +# +STARTUP_RCVAR_REGEX='[[:alpha:]_][[:alnum:]_]*="([Yy][Ee][Ss]|[Nn][Oo])"' + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCVAR_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcvar_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcvar_map [$var_to_set] +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.d scripts +# and their associated rcvar's. The map returned has the following format: +# +# rcvar default script description +# +# With each as follows: +# +# rcvar the variable used to enable this rc.d script +# default default value for this variable +# script the rc.d script in-question +# description description of the variable from rc.conf(5) defaults +# +# If $var_to_set is missing or NULL, the map is printed to standard output for +# capturing in a sub-shell (which is less-recommended because of performance +# degredation; for example, when called in a loop). +# +f_startup_rcvar_map() +{ + local __funcname=f_startup_rcvar_map + local __var_to_set="$1" + + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCVAR_MAP" ]; then + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCVAR_MAP" + else + echo "$STARTUP_RCVAR_MAP" + fi + return $SUCCESS + fi + + # + # create the in-memory cache (potentially from validated on-disk cache) + # + + # Get a list of /etc/rc.d scripts ... + local __file __rc_script_list= + for __file in "$ETC_RC_D"/*; do + [ -f "$__file" ] || continue + [ -x "$__file" ] || continue + __rc_script_list="$__rc_script_list $__file" + done + # ... and /usr/local/etc/rc.d scripts + __rc_script_list="$__rc_script_list $( + local_startup=$( f_sysrc_get local_startup ) + f_include "$ETC_RC_SUBR" + find_local_scripts_new + echo $local_rc + )" + __rc_script_list="${__rc_script_list# }" # Trim leading space + + # + # Calculate a digest given the checksums of all dependencies (scripts + # and the defaults file). This digest will be used to determine if an + # on-disk global persistent cache file (containg this digest on the + # first line) is valid and can be used to quickly populate the cache + # value for immediate return. + # + local __rc_script_list_digest + __rc_script_list_digest=$( cd "$ETC_RC_D" 2> /dev/null && + cksum "$RC_DEFAULTS" $__rc_script_list 2> /dev/null | md5 ) + + # + # Check to see if the global persistent cache file exists + # + if [ -f "$STARTUP_RCVAR_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # validated on-disk cache. If validation fails, fall-back to + # the current value and return error. + # + STARTUP_RCVAR_MAP=$( + ( # Get digest as first word on first line + read digest rest_ignored + + # + # If the stored digest matches the calculated- + # one populate the in-memory cache from the on- + # disk cache and return success. + # + if [ "$digest" = "$__rc_script_list_digest" ] + then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCVAR_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCVAR_MAP_CACHEFILE" + ) + local __retval=$? + export STARTUP_RCVAR_MAP # Make children faster (export cache) + if [ $__retval -eq $SUCCESS ]; then + export _STARTUP_RCVAR_MAP=1 + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCVAR_MAP" + else + echo "$STARTUP_RCVAR_MAP" + fi + return $SUCCESS + fi + # Otherwise, fall-thru to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistent + # cache file to speed up future executions). + # + + STARTUP_RCVAR_MAP=$( + for script in $__rc_script_list; do + rcvar_list=$( $script rcvar 2> /dev/null | awk -F= \ + -v script="$script" ' + /^'"$STARTUP_RCVAR_REGEX"'/ { + if ( $2 ~ /^"[Yy][Ee][Ss]"$/ ) + print $1 ",YES" + else + print $1 ",NO" + }' ) + for entry in $rcvar_list; do + rcvar="${entry%%,*}" + rcvar_default=$( f_sysrc_get_default "$rcvar" ) + [ "$rcvar_default" ] || + rcvar_default="${entry#*,}" + rcvar_desc=$( f_sysrc_desc "$rcvar" ) + echo $rcvar ${rcvar_default:-NO} \ + $script "$rcvar_desc" + done + done | sort -u + ) + export STARTUP_RCVAR_MAP + export _STARTUP_RCVAR_MAP=1 + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$STARTUP_RCVAR_MAP" + else + echo "$STARTUP_RCVAR_MAP" + fi + + # + # Attempt to create/update the persistent global cache + # + + # Create a new temporary file to write to + local __tmpfile + f_eval_catch -dk __tmpfile $__funcname mktemp \ + 'mktemp -t "%s"' "$__tmpfile" || return $FAILURE + + # Write the temporary file contents + echo "$__rc_script_list_digest" > "$__tmpfile" + echo "$STARTUP_RCVAR_MAP" >> "$__tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCVAR_MAP_CACHEFILE" in + */*) f_eval_catch -d $__funcname mkdir \ + 'mkdir -p "%s"' "${STARTUP_RCVAR_MAP_CACHEFILE%/*}" + esac + f_eval_catch -d $__funcname mv \ + 'mv "%s" "%s"' "$__tmpfile" "$STARTUP_RCVAR_MAP_CACHEFILE" +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." startup/rcvar.subr + +fi # ! $_STARTUP_RCVAR_SUBR |