diff --git a/usr.sbin/bsdconfig/USAGE b/usr.sbin/bsdconfig/USAGE index 86355b033bd3..69cd1b53abc4 100644 --- a/usr.sbin/bsdconfig/USAGE +++ b/usr.sbin/bsdconfig/USAGE @@ -31,6 +31,9 @@ Usage: @PROGRAM_NAME@ [OPTIONS] [command [OPTIONS]] OPTIONS: + -f file Load file as script and then exit. If multiple occurrences, + program will only exit after last occurrence. If file is a + single dash (`-'), @PROGRAM_NAME@ reads from standard input. -h Print usage statement and exit. -S Secure X11 mode (implies `-X'). As root, always prompt-for and validate sudo(8) username/password before starting. diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index 11ea8fc925bb..34cb3c383087 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -204,25 +204,45 @@ dialog_menu_main() ############################################################ MAIN # -# If $0 is not "bsdconfig", interpret it as a keyword to a menuitem +# If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or +# as a valid resword (see script.subr for additional details about reswords). # -if [ "$pgm" != "bsdconfig" ] && - indexfile=$( f_index_file "$pgm" ) && - cmd=$( f_index_menusel_command "$indexfile" "$pgm" ) -then - exec "$cmd" "$@" || exit 1 +if [ "$pgm" != "bsdconfig" ]; then + if indexfile=$( f_index_file "$pgm" ) && + cmd=$( f_index_menusel_command "$indexfile" "$pgm" ) + then + f_dprintf "pgm=[%s] indexfile=[%s] cmd=[%s]" \ + "$pgm" "$indexfile" "$cmd" + exec "$cmd" "$@" || exit 1 + else + f_include $BSDCFG_SHARE/script.subr + for resword in $RESWORDS; do + [ "$pgm" = "$resword" ] || continue + # Found a match + f_dprintf "pgm=[%s] A valid resWord!" "$pgm" + f_dispatch $resword + exit $? + done + fi fi # # Process command-line arguments # -while getopts hSX flag; do +scripts_loaded=0 +while getopts f:hSX flag; do case "$flag" in + f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr + f_script_load "$OPTARG" + scripts_loaded=$(( $scripts_loaded + 1 ));; h|\?) usage;; esac done shift $(( $OPTIND -1 )) +# If we've loaded any scripts, do not continue any further +[ $scripts_loaded -gt 0 ] && exit + # # Initialize # diff --git a/usr.sbin/bsdconfig/bsdconfig.8 b/usr.sbin/bsdconfig/bsdconfig.8 index 5bb3e73d7e9f..31773fd2da57 100644 --- a/usr.sbin/bsdconfig/bsdconfig.8 +++ b/usr.sbin/bsdconfig/bsdconfig.8 @@ -85,6 +85,17 @@ a master menu listing the available commands. .Pp The following options are available: .Bl -tag -width indent+ +.It Fl f Ar file +Load +.Ar file +as script and then exit. +If multiple occurrences, program will only exit after last occurrence. +If +.Ar file +is a single dash +.Pq Sq Fl , +.Nm +reads from standard input. .It Fl h Print usage statement and exit. .It Fl S diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index ec988adb0f77..dc7c246a05d6 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -34,6 +34,7 @@ msg_becoming_root_via_sudo="Becoming root via sudo(8)..." msg_cancel="Cancel" msg_cancel_exit="Cancel/Exit" msg_cannot_create_permission_denied="%s: cannot create %s: Permission denied" +msg_command_failed_rest_of_script_aborted="Command \`%s' failed - rest of script aborted." msg_created_path="Created %s" msg_directory_not_found="%s: Directory not found." msg_exit="Exit" @@ -56,6 +57,7 @@ msg_secure_mode_requires_x11="Secure-mode requires X11 (use \`-X')!" msg_secure_mode_requires_root="Secure-mode requires root-access!" msg_sorry_try_again="Sorry, try again." msg_try_sudo_only_this_once="Try sudo(8) only this once" +msg_unable_to_open="Unable to open %s" msg_unknown_user="Unknown user: %s" msg_usage="Usage" msg_user_disallowed="User disallowed: %s" diff --git a/usr.sbin/bsdconfig/share/Makefile b/usr.sbin/bsdconfig/share/Makefile index 7f299ec9c3ee..cd56dfc54437 100644 --- a/usr.sbin/bsdconfig/share/Makefile +++ b/usr.sbin/bsdconfig/share/Makefile @@ -3,7 +3,8 @@ NO_OBJ= FILESDIR= ${SHAREDIR}/bsdconfig -FILES= common.subr dialog.subr mustberoot.subr strings.subr sysrc.subr +FILES= common.subr dialog.subr mustberoot.subr script.subr \ + strings.subr sysrc.subr variable.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 9491fce0a99a..05531aad1307 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -114,6 +114,30 @@ f_have() f_quietly type "$@" } +# 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 (which is less-recommended +# because of performance degredation; for example, when called in a loop). +# +f_getvar() +{ + local var_to_get="$1" var_to_set="$2" + [ "$var_to_set" ] || local value + eval ${var_to_set:-value}=\"\${$var_to_get}\" + eval [ \"\${$var_to_get+set}\" ] + local retval=$? + eval f_dprintf '"f_getvar: var=[%s] value=[%s] r=%u"' \ + \"\$var_to_get\" \"\$${var_to_set:-value}\" \$retval + [ "$var_to_set" ] || { [ "$value" ] && echo "$value"; } + return $retval +} + # f_die [ $status [ $fmt [ $opts ... ]]] # # Abruptly terminate due to an error optionally displaying a message in a diff --git a/usr.sbin/bsdconfig/share/script.subr b/usr.sbin/bsdconfig/share/script.subr new file mode 100644 index 000000000000..d5a2e934e631 --- /dev/null +++ b/usr.sbin/bsdconfig/share/script.subr @@ -0,0 +1,139 @@ +if [ ! "$_SCRIPT_SUBR" ]; then _SCRIPT_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 (INLUDING, 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..." script.subr +f_include $BSDCFG_SHARE/variable.subr + +############################################################ GLOBALS + +RESWORDS= + +############################################################ FUNCTIONS + +# f_resword_new $resword $function +# +# Create a new `reserved' word for scripting purposes. Reswords call pre- +# defined functions but differ from those functions in the following ways: +# +# + Reswords do not take arguments but instead get all their data from +# the environment variable namespace. +# + Unless noError is set (must be non-NULL), if calling the resword +# results in failure, the application will terminate prematurely. +# + noError is unset after each/every resword is called. +# +# Reswords should not be used in bsdconfig itself (hence the name `reserved +# word') but instead only in scripts loaded through f_script_load()). +# +f_resword_new() +{ + local resword="$1" func="$2" + [ "$resword" ] || return $FAILURE + f_dprintf "script.subr: New resWord %s -> %s" "$resword" "$func" + eval $resword\(\){ f_dispatch $func $resword\; } + RESWORDS="$RESWORDS${RESWORDS:+ }$resword" +} + +# f_dispatch $func [$resword] +# +# Wrapper function used by `reserved words' (reswords) to call other functions. +# If $noError is set and non-NULL, a failure result from $func is ignored, +# otherwise the application is prematurely terminated using f_die(). +# +# NOTE: $noError is unset after every call. +# +f_dispatch() +{ + local func="$1" resword="${2:-$1}" + f_dprintf "f_dispatch: calling resword \`%s'" "$resword" + eval $func + local retval=$? _ignore_this_error + f_getvar $VAR_NO_ERROR _ignore_this_error + [ $retval -eq $SUCCESS ] || + [ "$_ignore_this_error" ] || f_die $retval \ + "$msg_command_failed_rest_of_script_aborted" "$resword" + unset $VAR_NO_ERROR +} + +# f_script_load [$file] +# +# Load a script (usually filled with reswords). If $file is missing or NULL, +# use one of the following instead (in order): +# +# $configFile +# install.cfg +# /stand/install.fg +# /tmp/install.cfg +# +# Unknown/unregistered reswords will generate sh(1) syntax errors but not cause +# premature termination. +# +# Returns success if a script was loaded and itself returned success. +# +f_script_load() +{ + local script="$1" config_file + + f_dprintf "f_script_load: script=[%s]" "$script" + if [ ! "$script" ]; then + f_getvar $VAR_CONFIG_FILE config_file + for script in \ + $config_file \ + install.cfg \ + /stand/install.cfg \ + /tmp/install.cfg \ + ; do + [ -e "$script" ] && break + done + elif [ "$script" = "-" ]; then + f_dprintf "f_script_load: Loading script from stdin" + eval "$( cat )" + else + f_dprintf "f_script_load: Loading script \`%s'" "$script" + if [ ! -e "$script" ]; then + f_show_msg "$msg_unable_to_open" "$script" + return $FAILURE + fi + . "$script" + fi +} + +############################################################ MAIN + +# +# Reserved words meant for scripting +# +f_resword_new dumpVariables f_dump_variables +f_resword_new loadConfig f_script_load + +f_dprintf "%s: Successfully loaded." script.subr + +fi # ! $_SCRIPT_SUBR diff --git a/usr.sbin/bsdconfig/share/variable.subr b/usr.sbin/bsdconfig/share/variable.subr new file mode 100644 index 000000000000..f63a5f8751f3 --- /dev/null +++ b/usr.sbin/bsdconfig/share/variable.subr @@ -0,0 +1,185 @@ +if [ ! "$_VARIABLE_SUBR" ]; then _VARIABLE_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 (INLUDING, 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..." variable.subr +f_include $BSDCFG_SHARE/dialog.subr + +############################################################ GLOBALS + +VARIABLES= + +# +# Default behavior is to call f_variable_set_defaults() when loaded. +# +: ${VARIABLE_SELF_INITIALIZE=1} + +# +# File to write when f_dump_variables() is called. +# +: ${VARIABLE_DUMPFILE:=/etc/bsdconfig.vars} + +############################################################ FUNCTIONS + +# f_variable_new $handle $variable +# +# Register a new variable named $variable with the given reference-handle +# $handle. The environment variable $handle is set to $variable allowing you to +# use the f_getvar() function (from common.subr) with $handle to get the value +# of environment variable $variable. For example: +# +# f_variable_new VAR_ABC abc +# +# allows the later indirection: +# +# f_getvar $VAR_ABC +# +# to return the value of environment variable `abc'. Variables registered in +# this manner are recorded in the $VARIABLES environment variable for later +# allowing dynamic enumeration of so-called `registered/advertised' variables. +# +f_variable_new() +{ + local handle="$1" variable="$2" + [ "$handle" ] || return $FAILURE + f_dprintf "variable.subr: New variable %s -> %s" "$handle" "$variable" + setvar $handle $variable + VARIABLES="$VARIABLES${VARIABLES:+ }$handle" +} + +# f_variable_get_value $var [ $fmt [ $opts ... ] ] +# +# Unless nonInteractive is set, prompt the user with a given value (pre-filled +# with the value of $var) and give them the chance to change the value. +# +# Unlike f_getvar() (from common.subr) which can return a variable to the +# caller on standard output, this function has no [meaningful] output. +# +# Returns success unless $var is either NULL or missing. +# +f_variable_get_value() +{ + local var="$1" cp + + [ "$var" ] || return $FAILURE + + if ! { f_getvar $var cp && ! f_interactive; }; then + shift 1 # var + cp=$( f_dialog_input "$( printf "$@" )" "$cp" ) && + setvar $var "$cp" + fi + + return $SUCCESS +} + +# f_variable_set_defaults +# +# Installs sensible defaults for registered/advertised variables. +# +f_variable_set_defaults() +{ + # + # Initialize various user-edittable values to their defaults + # + setvar $VAR_RELNAME "$UNAME_R" + + f_dprintf "f_variable_set_defaults: Defaults initialized." +} + +# f_dump_variables +# +# Dump a list of registered/advertised variables and their respective values to +# $VARIABLE_DUMPFILE. Returns success unless the file couldn't be written. If +# an error occurs, it is displayed using f_show_msg() (from common.subr). +# +f_dump_variables() +{ + local err sanitize_awk="{ gsub(/'/, \"'\\\\''\"); print }" + if ! err=$( + ( for handle in $VARIABLES; do + f_getvar $handle var || continue + f_getvar $var value || continue + value=$( echo "$value" | awk "$sanitize_awk" ) + printf "%s='%s'\n" "$var" "$value" + done > "$VARIABLE_DUMPFILE" ) 2>&1 + ); then + f_show_msg "%s" "$err" + return $FAILURE + fi +} + +# f_debugging +# +# Are we in debug mode? Returns success if extra DEBUG information has been +# requested (by setting $debug to non-NULL), otherwise false. +# +f_debugging() +{ + local value + f_getvar $VAR_DEBUG value && [ "$value" ] +} + +# f_interactive() +# +# Are we running interactively? Return error if $nonInteractive is set and non- +# NULL, otherwise return success. +# +f_interactive() +{ + local value + ! f_getvar $VAR_NONINTERACTIVE value || [ ! "$value" ] +} + +############################################################ MAIN + +# +# Variables that can be tweaked from config files +# +f_variable_new VAR_CONFIG_FILE configFile +f_variable_new VAR_DEBUG debug +f_variable_new VAR_DEBUG_FILE debugFile +f_variable_new VAR_NO_ERROR noError +f_variable_new VAR_NONINTERACTIVE nonInteractive +f_variable_new VAR_RELNAME releaseName + +# +# Self-initialize unless requested otherwise +# +f_dprintf "%s: VARIABLE_SELF_INITIALIZE=[%s]" \ + variable.subr "$VARIABLE_SELF_INITIALIZE" +case "$VARIABLE_SELF_INITIALIZE" in +""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;; +*) f_variable_set_defaults +esac + +f_dprintf "%s: Successfully loaded." variable.subr + +fi # ! $_VARIABLE_SUBR