summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bsdconfig/include
diff options
context:
space:
mode:
authordteske <dteske@FreeBSD.org>2012-07-14 03:16:57 +0000
committerdteske <dteske@FreeBSD.org>2012-07-14 03:16:57 +0000
commit3981b9b76aa0266598ee7b724e5981627d8ac129 (patch)
tree8b439d31cf63b5d5c97b653a3cd721fd9961baa5 /usr.sbin/bsdconfig/include
parent5d2a55de5070f6d3a8e4b9762a397596e7b308ae (diff)
downloadFreeBSD-src-3981b9b76aa0266598ee7b724e5981627d8ac129.zip
FreeBSD-src-3981b9b76aa0266598ee7b724e5981627d8ac129.tar.gz
Import bsdconfig(8) as a replacement for the post-install abilities of
deprecated sysinstall(8). NOTE: WITH_BSDCONFIG is currently required. Submitted by: Devin Teske (dteske), Ron McDowell <rcm@fuzzwad.org> Reviewed by: Ron McDowell <rcm@fuzzwad.org> Approved by: Ed Maste (emaste)
Diffstat (limited to 'usr.sbin/bsdconfig/include')
-rw-r--r--usr.sbin/bsdconfig/include/Makefile12
-rw-r--r--usr.sbin/bsdconfig/include/common.subr299
-rw-r--r--usr.sbin/bsdconfig/include/dialog.subr1441
-rw-r--r--usr.sbin/bsdconfig/include/messages.subr56
-rw-r--r--usr.sbin/bsdconfig/include/mustberoot.subr360
-rw-r--r--usr.sbin/bsdconfig/include/strings.subr104
-rw-r--r--usr.sbin/bsdconfig/include/sysrc.subr616
7 files changed, 2888 insertions, 0 deletions
diff --git a/usr.sbin/bsdconfig/include/Makefile b/usr.sbin/bsdconfig/include/Makefile
new file mode 100644
index 0000000..898359a
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+NO_OBJ=
+
+FILESDIR= ${LIBEXECDIR}/bsdconfig/include
+FILES= common.subr dialog.subr messages.subr mustberoot.subr \
+ strings.subr sysrc.subr
+
+beforeinstall:
+ mkdir -p ${DESTDIR}${FILESDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsdconfig/include/common.subr b/usr.sbin/bsdconfig/include/common.subr
new file mode 100644
index 0000000..c724c62
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/common.subr
@@ -0,0 +1,299 @@
+if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1
+#
+# Copyright (c) 2012 Ron McDowell
+# 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$
+#
+############################################################ GLOBALS
+
+#
+# Program name
+#
+pgm="${0##*/}"
+
+#
+# Program arguments
+#
+ARGC="$#"
+ARGV="$@"
+
+#
+# Global exit status variables
+#
+SUCCESS=0
+FAILURE=1
+
+############################################################ FUNCTIONS
+
+#
+# This is an empty function by default, to use it, copy
+# /usr/share/examples/bsdconfig/bsdconfigrc to $HOME/.bsdconfigrc
+#
+f_dprintf()
+{
+ : this page intentionally left blank
+}
+
+# f_err $fmt [ $opts ... ]
+#
+# Print a message to stderr (fd=2).
+#
+f_err()
+{
+ printf "$@" >&2
+}
+
+# f_quietly $command [ $arguments ... ]
+#
+# run a command quietly (quell any output to stdout or stderr)
+#
+f_quietly()
+{
+ "$@" > /dev/null 2>&1
+}
+
+# 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()
+{
+ f_quietly type "$@"
+}
+
+# f_die [ $status [ $fmt [ $opts ... ]]]
+#
+# Abruptly terminate due to an error optionally displaying a message in a
+# dialog box using printf(1) syntax.
+#
+f_die()
+{
+ local status=$FAILURE
+
+ # If there is at least one argument, take it as the status
+ if [ $# -gt 0 ]; then
+ status=$1
+ shift 1 # status
+ fi
+
+ # If there are still arguments left, pass them to f_show_msg
+ [ $# -gt 0 ] && f_show_msg "$@"
+
+ # Optionally call f_clean_up() function if it exists
+ f_have f_clean_up && f_clean_up
+
+ exit $status
+}
+
+# f_interrupt
+#
+# Interrupt handler.
+#
+f_interrupt()
+{
+ exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap
+ f_die
+}
+
+# f_show_msg $fmt [ $opts ... ]
+#
+# Display a message in a dialog box using printf(1) syntax.
+#
+f_show_msg()
+{
+ local msg
+ msg=$( printf "$@" )
+
+ #
+ # Use f_dialog_msgbox from dialog.subr if possible, otherwise fall
+ # back to dialog(1) (without options, making it obvious when using
+ # un-aided system dialog).
+ #
+ if f_have f_dialog_msgbox; then
+ f_dialog_msgbox "$msg"
+ else
+ dialog --msgbox "$msg" 0 0
+ fi
+}
+
+# f_include $file
+#
+# Include a shell subroutine file.
+#
+# If the subroutine file exists but returns error status during loading, exit
+# is called and execution is prematurely terminated with the same error status.
+#
+f_include()
+{
+ local file="$1"
+ . "$file" || exit $?
+}
+
+# f_include_lang $file
+#
+# Include a language file. Automatically takes $LANG and $LC_ALL into
+# considerationg when including $file (suffix ".$LC_ALL" or ".$LANG" will
+# automatically by added prior to loading the language file).
+#
+# No error is produced if (a) a language has been requested (by setting either
+# $LANG or $LC_ALL in the environment) and (b) the language file does not
+# exist -- in which case we will fall back to loading $file without-suffix.
+#
+# If the language file exists but returns error status during loading, exit
+# is called and execution is prematurely terminated with the same error status.
+#
+f_include_lang()
+{
+ local file="$1"
+ local lang="${LANG:-$LC_ALL}"
+
+ f_dprintf "lang=[$lang]"
+ if [ -f "$file.$lang" ]; then
+ . "$file.$lang" || exit $?
+ else
+ . "$file" || exit $?
+ fi
+}
+
+# f_usage $file [ $key1 $value1 ... ]
+#
+# Display USAGE file with optional pre-processor macro definitions. The first
+# argument is the template file containing the usage text to be displayed. If
+# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding"
+# will automatically be appended as a suffix to the provided $file pathname.
+#
+# When processing $file, output begins at the first line containing that is
+# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines
+# appearing after this first-line are output, including (a) comments (b) empty
+# lines, and (c) lines that are purely whitespace-only.
+#
+# If additional arguments appear after $file, substitutions are made while
+# printing the contents of the USAGE file. The pre-processor macro syntax is in
+# the style of autoconf(1), for example:
+#
+# f_usage $file "FOO" "BAR"
+#
+# Will cause instances of "@FOO@" appearing in $file to be replaced with the
+# text "BAR" before bering printed to the screen.
+#
+# 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_usage_awk='
+BEGIN { found = 0 }
+{
+ if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next
+ found = 1
+ print
+}
+'
+f_usage()
+{
+ local file="$1"
+ local lang="${LANG:-$LC_ALL}"
+
+ f_dprintf "lang=[$lang]"
+
+ shift 1 # file
+
+ local usage
+ if [ -f "$file.$lang" ]; then
+ usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE
+ else
+ usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE
+ fi
+
+ while [ $# -gt 0 ]; do
+ local key="$1"
+ export value="$2"
+ usage=$( echo "$usage" | awk \
+ "{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" )
+ shift 2
+ done
+
+ f_err "%s\n" "$usage"
+
+ exit $FAILURE
+}
+
+# f_index_menu_selection $file $pgm
+#
+# Process $file looking for $menu_selection values that correspond to $pgm.
+# This function is for internationalization (i18n) mapping of the on-disk
+# scriptname ($pgm) into the localized language (given language-specific
+# $file). If $LANG or $LC_ALL (in orderder of preference, respectively) is set,
+# ".encoding" will automatically be appended as a suffix to the provided $file
+# pathname.
+#
+# If, within $file, multiple $menu_selection values map to $pgm, only the first
+# one will be returned. If no mapping can be made, the NULL string is returned.
+#
+# If $file does not exist, error status is returned along with the NULL string.
+#
+# 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_index_menusel_awk='
+# Variables that should be defined on the invocation line:
+# -v pgm="program_name"
+#
+( $0 ~ "^menu_selection=.*\\|" pgm "\"" ) {
+ sub(/\|.*/, "")
+ sub(/^menu_selection="/, "")
+ print
+ exit
+}
+'
+f_index_menu_selection()
+{
+ local file="$1" pgm="$2"
+ local lang="${LANG:-$LC_ALL}"
+
+ f_dprintf "lang=[$lang]"
+
+ if [ -f "$file.$lang" ]; then
+ awk -v pgm="$pgm" "$f_index_menusel_awk" "$file.$lang" ||
+ exit $FAILURE
+ elif [ -f "$file" ]; then
+ awk -v pgm="$pgm" "$f_index_menusel_awk" "$file" ||
+ exit $FAILURE
+ else
+ return $FAILURE
+ fi
+}
+
+############################################################ MAIN
+
+#
+# Trap signals so we can recover gracefully
+#
+trap 'f_interrupt' SIGINT
+trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \
+ SIGFPE SIGTRAP SIGABRT SIGSEGV
+trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM
+
+fi # ! $_COMMON_SUBR
diff --git a/usr.sbin/bsdconfig/include/dialog.subr b/usr.sbin/bsdconfig/include/dialog.subr
new file mode 100644
index 0000000..35c71cb
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/dialog.subr
@@ -0,0 +1,1441 @@
+if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1
+#
+# Copyright (c) 2006-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_LIBE="/usr/libexec/bsdconfig"
+. $BSDCFG_LIBE/include/common.subr || exit 1
+f_include $BSDCFG_LIBE/include/strings.subr
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ CONFIGURATION
+
+#
+# Default directory to store dialog(1) temporary files
+#
+: ${DIALOG_TMPDIR:="/tmp"}
+
+############################################################ GLOBALS
+
+#
+# Default name of dialog(1) utility
+# NOTE: This is changed to "Xdialog" by the optional `-X' argument
+#
+DIALOG="dialog"
+
+#
+# Default dialog(1) title and backtitle text
+#
+DIALOG_TITLE="$pgm"
+DIALOG_BACKTITLE="bsdconfig"
+
+#
+# Settings used while interacting with dialog(1)
+#
+DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz"
+
+#
+# Declare that we are fully-compliant with Xdialog(1) by unset'ing all
+# compatibility settings.
+#
+unset XDIALOG_HIGH_DIALOG_COMPAT
+unset XDIALOG_FORCE_AUTOSIZE
+unset XDIALOG_INFOBOX_TIMEOUT
+
+#
+# Default behavior is to call f_dialog_init() automatically if not already
+# called manually by the time the first f_dialog_*() function is used.
+#
+: ${DIALOG_SELF_INITIALIZE=1}
+
+############################################################ GENERIC FUNCTIONS
+
+# f_dialog_title [$new_title]
+#
+# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1)
+# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first
+# argument is NULL, the current title is returned.
+#
+# Each time this function is called, a backup of the current values is made
+# allowing a one-time (single-level) restoration of the previous title using the
+# f_dialog_title_restore() function (below).
+#
+f_dialog_title()
+{
+ local new_title="$1"
+
+ if [ "$new_title" ]; then
+ if [ "$USE_XDIALOG" ]; then
+ _DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
+ DIALOG_BACKTITLE="$new_title"
+ else
+ _DIALOG_TITLE="$DIALOG_TITLE"
+ DIALOG_TITLE="$new_title"
+ fi
+ else
+ if [ "$USE_XDIALOG" ]; then
+ echo "$DIALOG_BACKTITLE"
+ else
+ echo "$DIALOG_TITLE"
+ fi
+ fi
+}
+
+# f_dialog_title_restore
+#
+# Restore the previous title set by the last call to f_dialog_title().
+# Restoration is non-recursive and only works to restore the most-recent title.
+#
+f_dialog_title_restore()
+{
+ if [ "$USE_XDIALOG" ]; then
+ DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
+ else
+ DIALOG_TITLE="$_DIALOG_TITLE"
+ fi
+}
+
+# f_dialog_backtitle [$new_backtitle]
+#
+# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of
+# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the
+# first argument is NULL, the current backtitle is returned.
+#
+f_dialog_backtitle()
+{
+ local new_backtitle="$1"
+
+ if [ "$new_backtitle" ]; then
+ if [ "$USE_XDIALOG" ]; then
+ _DIALOG_TITLE="$DIALOG_TITLE"
+ DIALOG_TITLE="$new_backtitle"
+ else
+ _DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
+ DIALOG_BACKTITLE="$new_backtitle"
+ fi
+ else
+ if [ "$USE_XDIALOG" ]; then
+ echo "$DIALOG_TITLE"
+ else
+ echo "$DIALOG_BACKTITLE"
+ fi
+ fi
+}
+
+# f_dialog_backtitle_restore
+#
+# Restore the previous backtitle set by the last call to f_dialog_backtitle().
+# Restoration is non-recursive and only works to restore the most-recent
+# backtitle.
+#
+f_dialog_backtitle_restore()
+{
+ if [ "$USE_XDIALOG" ]; then
+ DIALOG_TITLE="$_DIALOG_TITLE"
+ else
+ DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
+ fi
+}
+
+############################################################ SIZE FUNCTIONS
+
+# f_dialog_infobox_size $title $backtitle $prompt [$hline]
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--infobox' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, and [optionally] hline returning
+# the optimal width and height for the box (not exceeding the actual terminal
+# width or height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+# Output is in the format of "height width".
+#
+f_dialog_infobox_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
+ local min_width max_size
+
+ if [ "$USE_XDIALOG" ]; then
+ min_width=35
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_width=24
+ max_size=$( stty size ) # usually "24 80"
+ fi
+
+ local max_height="${max_size%%[$IFS]*}"
+ local max_width="${max_size##*[$IFS]}"
+ local height width=$min_width
+
+ #
+ # Bump width for long titles (but don't exceed terminal width).
+ #
+ n=$(( ${#title} + 4 ))
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ # Add 16.6% width for Xdialog(1)
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
+
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+
+ #
+ # For Xdialog(1), bump width for long backtitles (which appear within
+ # the window; don't exceed maximum width).
+ #
+ if [ "$USE_XDIALOG" ]; then
+ n=$(( ${#btitle} + 4 ))
+ n=$(( $n + $n / 6 ))
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ #
+ # Bump width for long prompts (if not already at maximum width).
+ #
+ if [ $width -lt $max_width ]; then
+ n=$( echo "$prompt" | f_longest_line_length )
+ n=$(( $n + 4 ))
+
+ # Add 16.6% width for Xdialog(1)
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
+
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ #
+ # Bump width for long hlines (if not already at maximum width).
+ # NOTE: Though Xdialog(1) supports `--hline', it's not currently used.
+ #
+ if [ ! "$USE_XDIALOG" ]; then
+ if [ $width -lt $max_width ]; then
+ n=$(( ${#hline} + 10 ))
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+ fi
+
+ #
+ # Set height based on number of rows in prompt
+ #
+ height=$( echo "$prompt" | f_number_of_lines )
+ height=$(( $height + 2 ))
+
+ #
+ # For Xdialog(1) bump height if backtitle is enabled (displayed in the
+ # X11 window with a separator line between the backtitle and msg text)
+ #
+ if [ "$USE_XDIALOG" -a "$btitle" ]; then
+ n=$( echo "$btitle" | f_number_of_lines )
+ height=$(( $height + $n + 2 ))
+ fi
+
+ # Make sure height is less than maximum screen size
+ [ $height -le $max_height ] || height=$max_height
+
+ # Return both
+ echo "$height $width"
+}
+
+# f_dialog_buttonbox_size $title $backtitle $prompt [$hline]
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--msgbox' and `--yesno' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, and [optionally] hline returning
+# the optimal width and height for the box (not exceeding the actual terminal
+# width or height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+# Output is in the format of "height width".
+#
+f_dialog_buttonbox_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4"
+ local size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local height="${size%%[$IFS]*}"
+ local width="${size##*[$IFS]}"
+
+ # Add height to accomodate the buttons
+ height=$(( $height + 2 ))
+
+ # Adjust for clipping with Xdialog(1) on Linux/GTK2
+ [ "$USE_XDIALOG" ] && height=$(( $height + 3 ))
+
+ #
+ # Enforce maximum height regardless
+ #
+ local max_size
+ if [ "$USE_XDIALOG" ]; then
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ max_size=$( stty size ) # usually "24 80"
+ fi
+ local max_height="${max_size%%[$IFS]*}"
+ [ $height -le $max_height ] || height=$max_height
+
+ # Return both
+ echo "$height $width"
+}
+
+# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline]
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--inputbox' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, initial text, and [optionally]
+# hline returning the optimal width and height for the box (not exceeding the
+# actual terminal width and height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+# Output is in the format of "height width".
+#
+f_dialog_inputbox_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n
+ local size="$( f_dialog_buttonbox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local height="${size%%[$IFS]*}"
+ local width="${size##*[$IFS]}"
+
+ local min_width max_size
+ if [ "$USE_XDIALOG" ]; then
+ min_width=35
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_width=24
+ max_size=$( stty size ) # usually "24 80"
+ fi
+ local max_height="${max_size%%[$IFS]*}"
+ local max_width="${max_size##*[$IFS]}"
+
+ #
+ # Add height to accomodate the input box
+ #
+ [ ! "$USE_XDIALOG" ] && height=$(( $height + 3 ))
+ [ $height -le $max_height ] || height=$max_height
+
+ #
+ # Bump width for initial text (if not already at maximum width).
+ # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
+ #
+ if [ $width -lt $max_width ]; then
+ n=$(( ${#init} + 7 ))
+
+ # Add 16.6% width for Xdialog(1)
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
+
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ # Return both
+ echo "$height $width"
+}
+
+# f_xdialog_2inputsbox_size $title $backtitle $prompt \
+# $label1 $init1 $label2 $init2
+#
+# Xdialog(1) does not perform auto-sizing of the width and height of
+# `--2inputsbox' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, label for the first field, initial
+# text for said field, label for the second field, and initial text for said
+# field returning the optimal width and height for the box (not exceeding the
+# actual terminal width and height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# Xdialog(1).
+#
+# Output is in the format of "height width".
+#
+f_xdialog_2inputsbox_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3"
+ local label1="$4" init1="$5" label2="$6" init2="$7" n
+ local size="$( f_dialog_inputbox_size \
+ "$title" "$btitle" "$prompt" "$init1" )"
+ local height="${size%%[$IFS]*}"
+ local width="${size##*[$IFS]}"
+
+ local min_width=35
+ local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ local max_height="${max_size%%[$IFS]*}"
+ local max_width="${max_size##*[$IFS]}"
+
+ # Add height for first label
+ height=$(( $height + 2 ))
+
+ #
+ # Bump width for first label text (if not already at maximum width).
+ #
+ if [ $width -lt $max_width ]; then
+ n=$(( ${#label1} + 7 ))
+
+ # Add 16.6% width for Xdialog(1)
+ n=$(( $n + $n / 6 ))
+
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ # Add height for second label
+ height=$(( $height + 2 ))
+
+ #
+ # Bump width for second label text (if not already at maximum width).
+ #
+ if [ $width -lt $max_width ]; then
+ n=$(( ${#label2} + 7 ))
+
+ # Add 16.6% width for Xdialog(1)
+ n=$(( $n + $n / 6 ))
+
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ # Add height for a second inputbox
+ height=$(( $height + 2 ))
+
+ #
+ # Bump width for second initial text (if not already at maximum width).
+ # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
+ #
+ if [ $width -lt $max_width ]; then
+ n=$(( ${#init2} + 7 ))
+
+ # Add 16.6% width for Xdialog(1)
+ n=$(( $n + $n / 6 ))
+
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ # Return both
+ echo "$height $width"
+}
+
+# f_dialog_menu_size $title $backtitle $prompt $hline \
+# $tag1 $item1 $tag2 $item2 ...
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--menu' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, hline and list of tag/item pairs,
+# returning the optimal width and height for the menu (not exceeding the actual
+# terminal width or height).
+#
+# Output is in the format of "height width rows".
+#
+f_dialog_menu_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
+ local min_width min_rows max_size
+
+ if [ "$USE_XDIALOG" ]; then
+ min_width=35
+ min_rows=1
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_width=24
+ min_rows=0
+ max_size=$( stty size ) # usually "24 80"
+ fi
+
+ local max_width="${max_size##*[$IFS]}"
+ local max_height="${max_size%%[$IFS]*}"
+ local box_size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local box_height="${box_size%%[$IFS]*}"
+ local box_width="${box_size##*[$IFS]}"
+ local max_rows=$(( $max_height - 8 ))
+ local height width=$box_width rows=$min_rows
+
+ shift 4 # title/btitle/prompt/hline
+
+ # If there's no prompt, bump the max-rows by 1
+ [ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
+
+ #
+ # The sum total between the longest tag-length and longest item-length
+ # should be used for the menu width (not to exceed terminal width).
+ #
+ # Also, calculate the number of rows (not to exceed terminal height).
+ #
+ local longest_tag=0 longest_item=0
+ while [ $# -ge 2 ]; do
+ local tag="$1" item="$2"
+ shift 2 # tag/item
+
+ [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
+ [ ${#item} -gt $longest_item ] && longest_item=${#item}
+ [ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
+ done
+
+ # Update width
+ n=$(( $longest_tag + $longest_item + 10 ))
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+
+ # Fix rows and set height
+ [ $rows -gt 0 ] || rows=1
+ if [ "$USE_XDIALOG" ]; then
+ height=$(( $rows + $box_height + 7 ))
+ else
+ height=$(( $rows + $box_height + 4 ))
+ fi
+ [ $height -le $max_height ] || height=$max_height
+
+ # Return all three
+ echo "$height $width $rows"
+}
+
+# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \
+# $tag1 $item1 $help1 $tag2 $item2 $help2 ...
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--menu' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, hline and list of tag/item/help
+# triplets, returning the optimal width and height for the menu (not exceeding
+# the actual terminal width or height).
+#
+# Output is in the format of "height width rows".
+#
+f_dialog_menu_with_help_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
+ local min_width min_rows max_size
+
+ if [ "$USE_XDIALOG" ]; then
+ min_width=35
+ min_rows=1
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_width=24
+ min_rows=0
+ max_size=$( stty size ) # usually "24 80"
+ fi
+
+ local max_width="${max_size##*[$IFS]}"
+ local max_height="${max_size%%[$IFS]*}"
+ local box_size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local box_height="${box_size%%[$IFS]*}"
+ local box_width="${box_size##*[$IFS]}"
+ local max_rows=$(( $max_height - 8 ))
+ local height width=$box_width rows=$min_rows
+
+ shift 4 # title/btitle/prompt/hline
+
+ # If there's no prompt, bump the max-rows by 1
+ [ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
+
+ #
+ # The sum total between the longest tag-length and longest item-length
+ # should be used for the menu width (not to exceed terminal width).
+ #
+ # Also, calculate the number of rows (not to exceed terminal height).
+ #
+ # Also, calculate the longest help while we're here. This will be used
+ # to influence the width of the menu if (and only-if) using Xdialog(1).
+ #
+ local longest_tag=0 longest_item=0 longest_help=0
+ while [ $# -ge 3 ]; do
+ local tag="$1" item="$2" help="$3"
+ shift 3 # tag/item/help
+
+ [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
+ [ ${#item} -gt $longest_item ] && longest_item=${#item}
+ [ ${#help} -gt $longest_help ] && longest_help=${#help}
+ [ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
+ done
+
+ # Update width
+ n=$(( $longest_tag + $longest_item + 10 ))
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+
+ # Update width for help text if using Xdialog(1)
+ if [ "$USE_XDIALOG" ]; then
+ n=$(( $longest_help + 10 ))
+ n=$(( $n + $n / 6 )) # +16.6%
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+ fi
+
+ # Fix rows and set height
+ [ $rows -gt 0 ] || rows=1
+ if [ "$USE_XDIALOG" ]; then
+ height=$(( $rows + $box_height + 8 ))
+ else
+ height=$(( $rows + $box_height + 4 ))
+ fi
+ [ $height -le $max_height ] || height=$max_height
+
+ # Return all three
+ echo "$height $width $rows"
+}
+
+# f_dialog_radiolist_size $title $backtitle $prompt $hline \
+# $tag1 $item1 $status1 $tag2 $item2 $status2 ...
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--radiolist' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, hline and list of tag/item/status
+# triplets, returning the optimal width and height for the radiolist (not
+# exceeding the actual terminal width or height).
+#
+# Output is in the format of "height width rows".
+#
+f_dialog_radiolist_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
+ local min_width min_rows max_size
+
+ if [ "$USE_XDIALOG" ]; then
+ min_width=35
+ min_rows=1
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_width=24
+ min_rows=0
+ max_size=$( stty size ) # usually "24 80"
+ fi
+
+ local max_width="${max_size##*[$IFS]}"
+ local max_height="${max_size%%[$IFS]*}"
+ local box_size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local box_height="${box_size%%[$IFS]*}"
+ local box_width="${box_size##*[$IFS]}"
+ local max_rows=$(( $max_height - 8 ))
+ local height width=$box_width rows=$min_rows
+
+ shift 4 # title/btitle/prompt/hline
+
+ #
+ # The sum total between the longest tag-length, longest item-length,
+ # and radio-button width should be used for the menu width (not to
+ # exceed terminal width).
+ #
+ # Also, calculate the number of rows (not to exceed terminal height).
+ #
+ local longest_tag=0 longest_item=0
+ while [ $# -ge 2 ]; do
+ local tag="$1" item="$2" help="$3"
+ shift 3 # tag/item/status
+
+ [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
+ [ ${#item} -gt $longest_item ] && longest_item=${#item}
+ [ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
+ done
+
+ # Update width
+ n=$(( $longest_tag + $longest_item + 13 ))
+ [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
+ if [ $n -gt $width -a $n -gt $min_width ]; then
+ if [ $n -lt $max_width ]; then
+ width=$n
+ else
+ width=$max_width
+ fi
+ fi
+
+ # Fix rows and set height
+ [ $rows -gt 0 ] || rows=1
+ if [ "$USE_XDIALOG" ]; then
+ height=$(( $rows + $box_height + 7 ))
+ else
+ height=$(( $rows + $box_height + 4 ))
+ fi
+ [ $height -le $max_height ] || height=$max_height
+
+ # Return all three
+ echo "$height $width $rows"
+}
+
+# f_dialog_calendar_size $title $backtitle $prompt [$hline]
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--calendar' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, and [optionally] hline returning
+# the optimal width and height for the box (not exceeding the actual terminal
+# width and height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+# Output is in the format of "height width".
+#
+f_dialog_calendar_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n
+ local size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local height="${size%%[$IFS]*}"
+ local width="${size##*[$IFS]}"
+
+ local min_width min_height max_size
+ if [ "$USE_XDIALOG" ]; then
+ min_height=15
+ min_width=55
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_height=0
+ min_width=40
+ max_size=$( stty size ) # usually "24 80"
+ fi
+ local max_height="${max_size%%[$IFS]*}"
+ local max_width="${max_size##*[$IFS]}"
+
+ #
+ # Enforce the minimum width for displaying the calendar
+ #
+ [ $width -ge $min_width ] || width=$min_width
+
+ #
+ # When using dialog(1), the calendar box is unique from other dialog(1)
+ # boxes in-that the height passed should not accomodate the 15-lines
+ # required to display the calendar. This does not apply to Xdialog(1).
+ #
+ # When using Xdialog(1), the height must accomodate the 15-lines
+ # required to display the calendar.
+ #
+ # NOTE: Also under dialog(1), because we can't predict whether the user
+ # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
+ # 16 rather than 15. This does not apply to Xdialog(1).
+ #
+ max_height=$(( $max_height - 16 ))
+ height=$( echo "$prompt" | f_number_of_lines )
+ if [ "$USE_XDIALOG" ]; then
+ # Add height to accomodate for the embedded calendar widget
+ height=$(( $height + $min_height - 1 ))
+
+ # Also, bump height if backtitle is enabled
+ if [ "$btitle" ]; then
+ local n="$( echo "$btitle" | f_number_of_lines )"
+ height=$(( $height + $n + 2 ))
+ fi
+ else
+ [ "$prompt" ] && height=$(( $height + 1 ))
+ fi
+ [ $height -le $max_height ] || height=$max_height
+
+ #
+ # The calendar box refuses to display if too large.
+ #
+ max_width=$(( $max_width - 2 ))
+ [ $width -le $max_width ] || width=$max_width
+
+ # Return both
+ echo "$height $width"
+}
+
+# f_dialog_timebox_size $title $backtitle $prompt [$hline]
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--timebox' boxes sensibly.
+#
+# This function helps solve this issue by taking as arguments (in order of
+# appearance) the title, backtitle, prompt, and [optionally] hline returning
+# the optimal width and height for the box (not exceeding the actual terminal
+# width and height).
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+# Output is in the format of "height width".
+#
+f_dialog_timebox_size()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local title="$1" btitle="$2" prompt="$3" hline="$4" n
+ local size="$( f_dialog_infobox_size \
+ "$title" "$btitle" "$prompt" "$hline" )"
+ local height="${size%%[$IFS]*}"
+ local width="${size##*[$IFS]}"
+
+ local min_width min_height max_size
+ if [ "$USE_XDIALOG" ]; then
+ min_width=40
+ max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ min_height=0
+ min_width=20
+ max_size=$( stty size ) # usually "24 80"
+ fi
+ local max_height="${max_size%%[$IFS]*}"
+ local max_width="${max_size##*[$IFS]}"
+
+ #
+ # Enforce the minimum width for displaying the timebox
+ #
+ [ $width -ge $min_width ] || width=$min_width
+
+ #
+ # When using dialog(1), the timebox box is unique from other dialog(1)
+ # boxes in-that the height passed should not accomodate the 6-lines
+ # required to display the timebox. This does not apply to Xdialog(1).
+ #
+ # When using Xdialog(1), the height seems to have no effect. All values
+ # provide the same results.
+ #
+ # NOTE: Also under dialog(1), because we can't predict whether the user
+ # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
+ # 7 rather than 6. This does not apply to Xdialog(1).
+ #
+ if [ "$USE_XDIALOG" ]; then
+ height=0 # Autosize; all values produce same results
+ else
+ max_height=$(( $max_height - 7 ))
+ height=$( echo "$prompt" | f_number_of_lines )
+ height=$(( $height + 1 ))
+ [ $height -le $max_height ] || height=$max_height
+ [ "$prompt" ] && height=$(( $height + 1 ))
+ fi
+
+ #
+ # The timebox box refuses to display if too large.
+ #
+ max_width=$(( $max_width - 2 ))
+ [ $width -le $max_width ] || width=$max_width
+
+ # Return both
+ echo "$height $width"
+}
+
+############################################################ CLEAR FUNCTIONS
+
+# f_dialog_clear
+#
+# Clears any/all previous dialog(1) displays.
+#
+f_dialog_clear()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ $DIALOG --clear
+}
+
+############################################################ INFO FUNCTIONS
+
+# f_dialog_info $info_text ...
+#
+# Throw up a dialog(1) infobox. The infobox remains until another dialog is
+# displayed or `dialog --clear' (or dialog_clear) is called.
+#
+f_dialog_info()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local info_text="$*"
+ local size="$( f_dialog_infobox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$info_text" )"
+
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ ${USE_XDIALOG:+--ignore-eof} \
+ ${USE_XDIALOG:+--no-buttons} \
+ --infobox \"\$info_text\" $size
+}
+
+# f_xdialog_info $info_text ...
+#
+# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces
+# EOF. This implies that you must execute this either as an rvalue to a pipe,
+# lvalue to indirection or in a sub-shell that provides data on stdin.
+#
+f_xdialog_info()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local info_text="$*"
+ local size="$( f_dialog_infobox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$info_text" )"
+
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --no-close --no-buttons \
+ --infobox \"\$info_text\" $size \
+ -1 # timeout of -1 means abort when EOF on stdin
+}
+
+############################################################ MSGBOX FUNCTIONS
+
+# f_dialog_msgbox $msg_text ...
+#
+# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER
+# or ESC, acknowledging the modal dialog.
+#
+# If the user presses ENTER, the exit status is zero (success), otherwise if
+# the user presses ESC the exit status is 255.
+#
+f_dialog_msgbox()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local msg_text="$*"
+ local size="$( f_dialog_buttonbox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg_text" )"
+
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --ok-label \"\$msg_ok\" \
+ --msgbox \"\$msg_text\" $size
+}
+
+############################################################ YESNO FUNCTIONS
+
+# f_dialog_yesno $msg_text ...
+#
+# Display a dialog(1) Yes/No prompt to allow the user to make some decision.
+# The yesno prompt remains until the user presses ENTER or ESC, acknowledging
+# the modal dialog.
+#
+# If the user chooses YES the exit status is zero, or chooses NO the exit
+# status is one, or presses ESC the exit status is 255.
+#
+f_dialog_yesno()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local msg_text="$*"
+ local hline="$hline_arrows_tab_enter"
+ local size="$( f_dialog_buttonbox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg_text" \
+ "$hline" )"
+
+ if [ "$USE_XDIALOG" ]; then
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_yes\" \
+ --cancel-label \"\$msg_no\" \
+ --yesno \"\$msg_text\" $size
+ else
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --yes-label \"\$msg_yes\" \
+ --no-label \"\$msg_no\" \
+ --yesno \"\$msg_text\" $size
+ fi
+}
+
+# f_dialog_noyes $msg_text ...
+#
+# Display a dialog(1) No/Yes prompt to allow the user to make some decision.
+# The noyes prompt remains until the user presses ENTER or ESC, acknowledging
+# the modal dialog.
+#
+# If the user chooses YES the exit status is zero, or chooses NO the exit
+# status is one, or presses ESC the exit status is 255.
+#
+# NOTE: This is just like the f_dialog_yesno function except "No" is default.
+#
+f_dialog_noyes()
+{
+ [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init
+
+ local msg_text="$*"
+ local hline="$hline_arrows_tab_enter"
+ local size="$( f_dialog_buttonbox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg_text" \
+ "$hline" )"
+
+ if [ "$USE_XDIALOG" ]; then
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --default-no \
+ --ok-label \"\$msg_yes\" \
+ --cancel-label \"\$msg_no\" \
+ --yesno \"\$msg_text\" $size
+ else
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --defaultno \
+ --yes-label \"\$msg_yes\" \
+ --no-label \"\$msg_no\" \
+ --yesno \"\$msg_text\" $size
+ fi
+}
+
+############################################################ INPUT FUNCTIONS
+
+# f_dialog_inputstr
+#
+# Obtain the inputstr entered by the user from the most recently displayed
+# dialog(1) inputbox and clean up any temporary files.
+#
+f_dialog_inputstr()
+{
+ local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$"
+
+ [ -f "$tmpfile" ] || return $FAILURE
+
+ # Skip warnings and trim leading/trailing whitespace from user input
+ awk '
+ BEGIN { found = 0 }
+ {
+ if ( ! found )
+ {
+ if ( $0 ~ /^$/ ) next
+ if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
+ found = 1
+ }
+ sub(/^[[:space:]]*/, "")
+ sub(/[[:space:]]*$/, "")
+ print
+ }
+ ' "$tmpfile" 2> /dev/null
+ f_quietly rm -f "$tmpfile"
+
+ return $SUCCESS
+}
+
+############################################################ MENU FUNCTIONS
+
+# f_dialog_menutag
+#
+# Obtain the menutag chosen by the user from the most recently displayed
+# dialog(1) menu and clean up any temporary files.
+#
+f_dialog_menutag()
+{
+ local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$"
+
+ [ -f "$tmpfile" ] || return $FAILURE
+
+ awk '
+ BEGIN { found = 0 }
+ {
+ if ( found ) # ... just spew
+ {
+ print
+ next
+ }
+ if ( $0 ~ /^$/ ) next
+ if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
+ found = 1
+ print
+ }
+ ' "$tmpfile" 2> /dev/null
+ f_quietly rm -f "$tmpfile"
+
+ return $SUCCESS
+}
+
+# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ...
+#
+# To use the `--menu' option of dialog(1) you must pass an ordered list of
+# tag/item pairs on the command-line. When the user selects a menu option the
+# tag for that item is printed to stderr.
+#
+# This function allows you to dereference the tag chosen by the user back into
+# the item associated with said tag.
+#
+# Pass the tag chosen by the user as the first argument, followed by the
+# ordered list of tag/item pairs (HINT: use the same tag/item list as was
+# passed to dialog(1) for consistency).
+#
+# If the tag cannot be found, NULL is returned.
+#
+f_dialog_menutag2item()
+{
+ local tag="$1" tagn item
+ shift 1 # tag
+
+ while [ $# -gt 0 ]; do
+ tagn="$1"
+ item="$2"
+ shift 2 # tagn/item
+
+ if [ "$tag" = "$tagn" ]; then
+ echo "$item"
+ return $SUCCESS
+ fi
+ done
+ return $FAILURE
+}
+
+# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \
+# $tag2 $item2 $help2 ...
+#
+# To use the `--menu' option of dialog(1) with the `--item-help' option, you
+# must pass an ordered list of tag/item/help triplets on the command-line. When
+# the user selects a menu option the tag for that item is printed to stderr.
+#
+# This function allows you to dereference the tag chosen by the user back into
+# the item associated with said tag (help is discarded/ignored).
+#
+# Pass the tag chosen by the user as the first argument, followed by the
+# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
+# as was passed to dialog(1) for consistency).
+#
+# If the tag cannot be found, NULL is returned.
+#
+f_dialog_menutag2item_with_help()
+{
+ local tag="$1" tagn item
+ shift 1 # tag
+
+ while [ $# -gt 0 ]; do
+ tagn="$1"
+ item="$2"
+ shift 3 # tagn/item/help
+
+ if [ "$tag" = "$tagn" ]; then
+ echo "$item"
+ return $SUCCESS
+ fi
+ done
+ return $FAILURE
+}
+
+# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ...
+#
+# To use the `--menu' option of dialog(1) you must pass an ordered list of
+# tag/item pairs on the command-line. When the user selects a menu option the
+# tag for that item is printed to stderr.
+#
+# This function allows you to dereference the tag chosen by the user back into
+# the index associated with said tag. The index is the one-based tag/item pair
+# array position within the ordered list of tag/item pairs passed to dialog(1).
+#
+# Pass the tag chosen by the user as the first argument, followed by the
+# ordered list of tag/item pairs (HINT: use the same tag/item list as was
+# passed to dialog(1) for consistency).
+#
+# If the tag cannot be found, NULL is returned.
+#
+f_dialog_menutag2index()
+{
+ local tag="$1" tagn n=1
+ shift 1 # tag
+
+ while [ $# -gt 0 ]; do
+ tagn="$1"
+ shift 2 # tagn/item
+
+ if [ "$tag" = "$tagn" ]; then
+ echo $n
+ return $SUCCESS
+ fi
+ n=$(( $n + 1 ))
+ done
+ return $FAILURE
+}
+
+# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \
+# $tag2 $item2 $help2 ...
+#
+# To use the `--menu' option of dialog(1) with the `--item-help' option, you
+# must pass an ordered list of tag/item/help triplets on the command-line. When
+# the user selects a menu option the tag for that item is printed to stderr.
+#
+# This function allows you to dereference the tag chosen by the user back into
+# the index associated with said tag. The index is the one-based tag/item/help
+# triplet array position within the ordered list of tag/item/help triplets
+# passed to dialog(1).
+#
+# Pass the tag chosen by the user as the first argument, followed by the
+# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
+# as was passed to dialog(1) for consistency).
+#
+# If the tag cannot be found, NULL is returned.
+#
+f_dialog_menutag2index_with_help()
+{
+ local tag="$1" tagn n=1
+ shift 1 # tag
+
+ while [ $# -gt 0 ]; do
+ tagn="$1"
+ shift 3 # tagn/item/help
+
+ if [ "$tag" = "$tagn" ]; then
+ echo $n
+ return $SUCCESS
+ fi
+ n=$(( $n + 1 ))
+ done
+ return $FAILURE
+}
+
+############################################################ INIT FUNCTIONS
+
+# f_dialog_init
+#
+# Initialize (or re-initialize) the dialog module after setting/changing any
+# of the following environment variables:
+#
+# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
+# that Xdialog(1) should be used instead of dialog(1).
+#
+# SECURE Either NULL or Non-NULL. If given a value will indicate
+# that (while running as root) sudo(8) authentication is
+# required to proceed.
+#
+f_dialog_init()
+{
+ DIALOG_SELF_INITIALIZE=
+
+ #
+ # Process stored command-line arguments
+ #
+ SECURE=$( set -- "$ARGV"
+ while getopts S flag > /dev/null; do
+ case "$flag" in
+ S) echo 1;;
+ \?) continue;;
+ esac
+ done
+ )
+ USE_XDIALOG=$( set -- "$ARGV"
+ while getopts SX flag > /dev/null; do
+ case "$flag" in
+ S|X) echo 1;;
+ \?) continue;;
+ esac
+ done
+ )
+
+ #
+ # Process `-X' command-line option
+ #
+ [ "$USE_XDIALOG" ] && DIALOG=Xdialog
+
+ #
+ # Sanity check, or die gracefully
+ #
+ if ! f_have $DIALOG; then
+ unset USE_XDIALOG
+ failed_dialog="$DIALOG"
+ DIALOG=dialog
+ f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
+ fi
+
+ #
+ # If we're already running as root but we got there by way of sudo(8)
+ # and we have X11, we should merge the xauth(1) credentials from our
+ # original user.
+ #
+ if [ "$USE_XDIALOG" ] &&
+ [ "$( id -u )" = "0" ] &&
+ [ "$SUDO_USER" -a "$DISPLAY" ]
+ then
+ if ! f_have xauth; then
+ # Die gracefully, as we [likely] can't use Xdialog(1)
+ unset USE_XDIALOG
+ DIALOG=dialog
+ f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth"
+ fi
+ HOSTNAME=$(hostname)
+ displaynum="${DISPLAY#*:}"
+ eval xauth -if \~$SUDO_USER/.Xauthority extract - \
+ \"\$HOSTNAME/unix:\$displaynum\" \
+ \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \
+ ~root/.Xauthority merge - > /dev/null 2>&1'
+ fi
+
+ #
+ # Probe Xdialog(1) for maximum height/width constraints, or die
+ # gracefully
+ #
+ if [ "$USE_XDIALOG" ]; then
+ if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 )
+ then
+ # Xdialog(1) failed, fall back to dialog(1)
+ unset USE_XDIALOG
+ size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$maxsize" "" )
+ eval dialog \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --ok-label \"\$msg_ok\" \
+ --msgbox \"\$maxsize\" $size
+ exit $FAILURE
+ fi
+
+ XDIALOG_MAXSIZE=$(
+ set -- ${maxsize##*:}
+
+ height=${1%,}
+ width=$2
+
+ echo $height $width
+ )
+ unset maxsize
+ fi
+
+ #
+ # If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE.
+ # The reason for this is because many dialog(1) applications use
+ # --backtitle for the program name (which is better suited as
+ # --title with Xdialog(1)).
+ #
+ if [ "$USE_XDIALOG" ]; then
+ _DIALOG_TITLE="$DIALOG_TITLE"
+ DIALOG_TITLE="$DIALOG_BACKTITLE"
+ DIALOG_BACKTITLE="$_DIALOG_TITLE"
+ unset _DIALOG_TITLE
+ fi
+}
+
+############################################################ CLEAN-UP FUNCTIONS
+
+# f_clean_up
+#
+# Clean-up routines (run when script exits or is killed).
+#
+f_clean_up()
+{
+ f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$
+}
+
+############################################################ MAIN
+
+#
+# Trap signals so we can recover gracefully
+#
+trap 'f_clean_up' EXIT
+
+fi # ! $_DIALOG_SUBR
diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr
new file mode 100644
index 0000000..282d936
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/messages.subr
@@ -0,0 +1,56 @@
+# Copyright (c) 2012 Ron McDowell
+# 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$
+
+field_username="Username:"
+field_password="Password:"
+hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER"
+hline_arrows_tab_enter="Press arrows, TAB or ENTER"
+msg_cancel="Cancel"
+msg_cannot_create_permission_denied="%s: cannot create %s: Permission denied"
+msg_directory_not_found="%s: Directory not found."
+msg_exit="Exit"
+msg_exit_bsdconfig="Exit bsdconfig"
+msg_help_text="This menu allows you to configure your system after the installation\nprocess is complete. At the minimum, you should probably set the root\npassword and the system time zone.\n\nFor extra goodies like bash, emacs, firefox, etc., you should look at the\n'Packages' item in this menu.\n\nFor setting the timezone after the system is installed, use the 'Time\nZone' item in this menu.\n\nFor more information on the overall general system configuration, see the\n/etc/rc.conf and /etc/defaults/rc.conf files. (Note: to inhibit displaying\nof this message, create an empty file: \$HOME/.bsdconfigrc or copy it from\n/usr/share/examples/bsdconfig/bsdconfigrc)"
+msg_main_menu="Main Menu"
+msg_menu_text="If you've already installed FreeBSD, you may use\nthis menu to customize it somewhat to suit your\nparticular configuration. Most importantly, you\ncan use the Packages utility to load extra '3rd\nparty' software not provided in the base\ndistributions."
+msg_must_be_root_to_execute="%s: must be root to execute"
+msg_nfailed_attempts="%u incorrect password attempts"
+msg_no="No"
+msg_no_such_file_or_directory="%s: %s: No such file or directory"
+msg_no_username="No username provided!"
+msg_not_found="not found"
+msg_ok="OK"
+msg_permission_denied="%s: %s: permission denied"
+msg_please_enter_password="Please enter your password for sudo(8):"
+msg_please_enter_username_password="Please enter a username and password for sudo(8):"
+msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax errors"
+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_unknown_user="Unknown user: %s"
+msg_user_disallowed="User disallowed: %s"
+msg_yes="Yes"
diff --git a/usr.sbin/bsdconfig/include/mustberoot.subr b/usr.sbin/bsdconfig/include/mustberoot.subr
new file mode 100644
index 0000000..c646530
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/mustberoot.subr
@@ -0,0 +1,360 @@
+if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
+#
+# Copyright (c) 2006-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_LIBE="/usr/libexec/bsdconfig"
+. $BSDCFG_LIBE/include/common.subr || exit 1
+f_include $BSDCFG_LIBE/include/dialog.subr
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ CONFIGURATION
+# NOTE: These are not able to be overridden/inherited for security purposes.
+
+#
+# Number of tries a user gets to enter his/her password before we log the
+# sudo(8) failure and exit.
+#
+PASSWD_TRIES=3
+
+#
+# While in SECURE mode, should authentication as `root' be allowed? Set to
+# non-NULL to enable authentication as `root', otherwise disabled.
+#
+# WARNING:
+# Unless using a custom sudo(8) configuration, user `root' should not be
+# allowed because no password is required to become `root' when already `root'
+# and therefore, any value entered as password will work.
+#
+SECURE_ALLOW_ROOT=
+
+#
+# While in SECURE mode, should we divulge (through error message) when the
+# requested authentication user does not exist? Set to non-NULL to enable,
+# otherwise a non-existent user is treated like an invalid password.
+#
+SECURE_DIVULGE_UNKNOWN_USER=
+
+############################################################ FUNCTIONS
+
+# f_become_root_via_sudo
+#
+# If not running as root, prompt for sudo(8) credentials to become root.
+# Re-execution of the current program via sudo is automatically handled.
+#
+# The following environment variables effect functionality:
+#
+# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
+# that Xdialog(1) should be used instead of dialog(1).
+#
+f_become_root_via_sudo()
+{
+ local msg hline size
+
+ [ "$( id -u )" = "0" ] && return $SUCCESS
+
+ f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
+
+ #
+ # Check sudo(8) access before prompting for password.
+ #
+ :| sudo -S -v 2> /dev/null
+ if [ $? -ne $SUCCESS ]; then
+ #
+ # sudo(8) access denied. Prompt for their password.
+ #
+ msg="$msg_please_enter_password"
+ hline="$hline_alnum_punc_tab_enter"
+ size=$( f_dialog_inputbox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg" \
+ "$hline" )
+
+ #
+ # Continue prompting until they either Cancel, succeed
+ # or exceed the number of allowed failures.
+ #
+ local password nfailures=0 retval
+ while [ $nfailures -lt $PASSWD_TRIES ]; do
+ if [ "$USE_XDIALOG" ]; then
+ password=$( $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --password --inputbox "$msg" $size \
+ 2>&1 > /dev/null )
+ retval=$?
+
+ # Catch X11-related errors
+ [ $retval -eq 255 ] &&
+ f_die $retval "$password"
+ else
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --insecure \
+ --passwordbox "$msg" $size \
+ 2> "$DIALOG_TMPDIR/dialog.inputbox.$$"
+ retval=$?
+ password=$( f_dialog_inputstr )
+ fi
+
+ # Exit if the user cancelled.
+ [ $retval -eq $SUCCESS ] || exit $retval
+
+ #
+ # Validate sudo(8) credentials
+ #
+ sudo -S -v 2> /dev/null <<-EOF
+ $password
+ EOF
+ retval=$?
+ unset password # scrub memory
+ if [ $retval -eq $SUCCESS ]; then
+ # Access granted...
+ break
+ else
+ # Access denied...
+ nfailures=$(( $nfailures + 1 ))
+
+ # introduce a short delay
+ if [ $nfailures -lt $PASSWD_TRIES ]; then
+ f_dialog_info "$msg_sorry_try_again"
+ sleep 1
+ fi
+ fi
+ done
+
+ #
+ # If user exhausted number of allowed password tries, log
+ # the security event and exit immediately.
+ #
+ if [ $nfailures -ge $PASSWD_TRIES ]; then
+ msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
+ logger -p auth.notice -t sudo " " \
+ "$USER : $msg" \
+ "; TTY=$(tty)" \
+ "; PWD=$PWD" \
+ "; USER=root" \
+ "; COMMAND=$0"
+ f_die 1 "sudo: $msg"
+ fi
+ fi
+
+ # Use xauth(1) to grant root the ability to use this X11/SSH session
+ if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
+ f_have xauth || f_die 1 \
+ "$msg_no_such_file_or_directory" "$pgm" "xauth"
+ local HOSTNAME displaynum
+ HOSTNAME=$(hostname)
+ displaynum="${DISPLAY#*:}"
+ xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
+ $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
+ ~root/.Xauthority merge - > /dev/null 2>&1'
+ fi
+
+ # Re-execute ourselves with sudo(8)
+ if [ $ARGC -gt 0 ]; then
+ exec sudo "$0" $ARGV
+ else
+ exec sudo "$0"
+ fi
+ exit $? # Never reached unless error
+}
+
+# f_authenticate_some_user
+#
+# Only used if running as root and requires X11 (see USE_XDIALOG below).
+# Prompts the user to enter a username and password to be authenticated via
+# sudo(8) to proceed.
+#
+# The following environment variables effect functionality:
+#
+# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
+# that Xdialog(1) should be used instead of dialog(1).
+#
+f_authenticate_some_user()
+{
+ local msg hline size width height
+
+ f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
+
+ #
+ # Secure-mode has been requested.
+ #
+
+ [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
+ [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
+
+ #
+ # Prompt for sudo(8) credentials.
+ #
+
+ msg="$msg_please_enter_username_password"
+ hline="$hline_alnum_punc_tab_enter"
+ size=$( f_xdialog_2inputsbox_size \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg" \
+ "$field_username" "" \
+ "$field_password" "" )
+ width="${size##*[$IFS]}"
+ height="${size%%[$IFS]*}"
+ height=$(( $height + 2 )) # Add height for --password
+
+ #
+ # Continue prompting until they either Cancel, succeed or exceed the
+ # number of allowed failures.
+ #
+ local user_pass nfailures=0 retval
+ while [ $nfailures -lt $PASSWD_TRIES ]; do
+ user_pass=$( $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --password --2inputsbox "$msg" \
+ $height $width \
+ "$field_username" "" \
+ "$field_password" "" \
+ 2>&1 > /dev/null )
+ retval=$?
+
+ # Catch X11-related errors
+ [ $retval -eq 255 ] && f_die $retval "$user_pass"
+
+ # Exit if the user cancelled.
+ [ $retval -eq $SUCCESS ] || exit $retval
+
+ #
+ # Make sure the user exists and is non-root
+ #
+ local user password
+ user="${user_pass%%/*}"
+ password="${user_pass#*/}"
+ unset user_pass # scrub memory
+ if [ ! "$user" ]; then
+ nfailures=$(( $nfailures + 1 ))
+ f_dialog_msgbox "$msg_no_username"
+ continue
+ fi
+ if [ ! "$SECURE_ALLOW_ROOT" ]; then
+ case "$user" in
+ root|toor)
+ nfailures=$(( $nfailures + 1 ))
+ f_dialog_msgbox "$( printf \
+ "$msg_user_disallowed" "$user" )"
+ continue
+ esac
+ fi
+ if ! f_quietly id "$user"; then
+ nfailures=$(( $nfailures + 1 ))
+ if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
+ f_dialog_msgbox "$( printf \
+ "$msg_unknown_user" "$user" )"
+ elif [ $nfailures -lt $PASSWD_TRIES ]; then
+ f_dialog_info "$msg_sorry_try_again"
+ sleep 1
+ fi
+ continue
+ fi
+
+ #
+ # Validate sudo(8) credentials for given user
+ #
+ su -m "$user" <<-EOF
+ sh <<EOS
+ sudo -k
+ sudo -S -v 2> /dev/null <<EOP
+ $password
+ EOP
+ EOS
+ EOF
+ retval=$?
+ unset user
+ unset password # scrub memory
+
+ if [ $retval -eq $SUCCESS ]; then
+ # Access granted...
+ break
+ else
+ # Access denied...
+ nfailures=$(( $nfailures + 1 ))
+
+ # introduce a short delay
+ if [ $nfailures -lt $PASSWD_TRIES ]; then
+ f_dialog_info "$msg_sorry_try_again"
+ sleep 1
+ fi
+ fi
+ done
+
+ #
+ # If user exhausted number of allowed password tries, log
+ # the security event and exit immediately.
+ #
+ if [ $nfailures -ge $PASSWD_TRIES ]; then
+ msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
+ logger -p auth.notice -t sudo " " \
+ "${SUDO_USER:-$USER} : $msg" \
+ "; TTY=$(tty)" \
+ "; PWD=$PWD" \
+ "; USER=root" \
+ "; COMMAND=$0"
+ f_die 1 "sudo: $message"
+ fi
+}
+
+# f_mustberoot_init
+#
+# If not already root, make the switch to root by re-executing ourselves via
+# sudo(8) using user-supplied credentials.
+#
+# The following environment variables effect functionality:
+#
+# SECURE Either NULL or Non-NULL. If given a value will indicate
+# that (while running as root) sudo(8) authentication is
+# required to proceed.
+#
+f_mustberoot_init()
+{
+ if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
+ f_become_root_via_sudo
+ elif [ "$SECURE" ]; then
+ f_authenticate_some_user
+ fi
+}
+
+fi # ! $_MUSTBEROOT_SUBR
diff --git a/usr.sbin/bsdconfig/include/strings.subr b/usr.sbin/bsdconfig/include/strings.subr
new file mode 100644
index 0000000..1a330ad
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/strings.subr
@@ -0,0 +1,104 @@
+if [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
+#
+# Copyright (c) 2006-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$
+
+# f_substr "$string" $start [ $length ]
+#
+# Simple wrapper to awk(1)'s `substr' function.
+#
+f_substr()
+{
+ local string="$1" start="${2:-0}" len="${3:-0}"
+ echo "$string" | awk "{ print substr(\$0, $start, $len) }"
+}
+
+# f_longest_line_length
+#
+# Simple wrapper to an awk(1) script to print the length of the longest line of
+# input (read from stdin). Supports the newline escape-sequence `\n' for
+# splitting a single line into multiple lines.
+#
+f_longest_line_length_awk='
+BEGIN { longest = 0 }
+{
+ if (split($0, lines, /\\n/) > 1)
+ {
+ for (n in lines)
+ {
+ len = length(lines[n])
+ longest = ( len > longest ? len : longest )
+ }
+ }
+ else
+ {
+ len = length($0)
+ longest = ( len > longest ? len : longest )
+ }
+}
+END { print longest }
+'
+f_longest_line_length()
+{
+ awk "$f_longest_line_length_awk"
+}
+
+# f_number_of_lines
+#
+# Simple wrapper to an awk(1) script to print the number of lines read from
+# stdin. Supports newline escape-sequence `\n' for splitting a single line into
+# multiple lines.
+#
+f_number_of_lines_awk='
+BEGIN { num_lines = 0 }
+{
+ num_lines += split($0, unused, /\\n/)
+}
+END { print num_lines }
+'
+f_number_of_lines()
+{
+ awk "$f_number_of_lines_awk"
+}
+
+# f_isinteger $arg
+#
+# Returns true if argument is a positive/negative whole integer.
+#
+f_isinteger()
+{
+ local arg="$1"
+
+ # Prevent division-by-zero
+ [ "$arg" = "0" ] && return $SUCCESS
+
+ # Attempt to perform arithmetic divison (an operation which will exit
+ # with error unless arg is a valid positive/negative whole integer).
+ #
+ ( : $((0/$arg)) ) > /dev/null 2>&1
+}
+
+fi # ! $_STRINGS_SUBR
diff --git a/usr.sbin/bsdconfig/include/sysrc.subr b/usr.sbin/bsdconfig/include/sysrc.subr
new file mode 100644
index 0000000..52e57bc
--- /dev/null
+++ b/usr.sbin/bsdconfig/include/sysrc.subr
@@ -0,0 +1,616 @@
+if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
+#
+# Copyright (c) 2006-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_LIBE="/usr/libexec/bsdconfig"
+. $BSDCFG_LIBE/include/common.subr || exit 1
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ CONFIGURATION
+
+#
+# Standard pathnames (inherit values from shell if available)
+#
+: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
+
+############################################################ GLOBALS
+
+#
+# Global exit status variables
+#
+SUCCESS=0
+FAILURE=1
+
+############################################################ 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).
+#
+# 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 RC_CONFS RC_DEFAULTS SUCCESS
+
+ . "$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().
+ #
+ ( : ${RC_CONFS?} ) > /dev/null 2>&1
+ if [ $? -eq ${SUCCESS:-0} ]; then
+ rc_conf_files="$RC_CONFS"
+ _rc_confs_set=1
+ fi
+
+ unset SUCCESS
+ # no longer needed
+
+ source_rc_confs > /dev/null 2>&1
+
+ #
+ # If the query was for `rc_conf_files' AND after calling
+ # source_rc_confs the vaue 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 echo '"${'"$1"'}"' 2> /dev/null
+ )
+}
+
+# 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 echo '"${'"$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"
+ local regex="^[[:space:]]*$varname="
+ local rc_conf_files="$( f_sysrc_get rc_conf_files )"
+ local conf_files=
+ local file
+
+ # Check parameters
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # If RC_CONFS is defined, set $rc_conf_files to an explicit
+ # value, modifying the default behavior of source_rc_confs().
+ #
+ [ "$RC_CONFS" ] && 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 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" ]; then
+ file="${RC_CONFS%%[$IFS]*}"
+ else
+ file=$( f_sysrc_get rc_conf_files )
+ file="${file%%[$IFS]*}"
+ fi
+ fi
+
+ #
+ # If not found, append new value to last file and return.
+ #
+ if [ "$not_found" ]; then
+ 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="$( mktemp -t "$pgm" )"
+ [ "$tmpfile" ] || return $FAILURE
+
+ #
+ # 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
+ mode=$( stat -f '%#Lp' "$file" 2> /dev/null )
+ f_quietly chmod "${mode:-0644}" "$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
+ owner=$( stat -f '%u:%g' "$file" 2> /dev/null )
+ f_quietly chown "${owner:-root:wheel}" "$tmpfile"
+
+ #
+ # Operate on the matching file, replacing only the last occurrence.
+ #
+ local new_contents retval
+ new_contents=$( tail -r $file 2> /dev/null )
+ 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 ! /bin/sh -n "$tmpfile"; then
+ f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Finally, move the temporary file into place.
+ #
+ mv "$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 varname="$1"
+ local file
+
+ # Check arguments
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # Operate on each of the specified files
+ #
+ for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do
+ [ -e "$file" ] || continue
+
+ #
+ # Create a new temporary file to write to.
+ #
+ local tmpfile="$( mktemp -t "$pgm" )"
+ [ "$tmpfile" ] || return $FAILURE
+
+ #
+ # Fixup permissions and ownership (mktemp(1) defaults to 0600
+ # permissions) to instead match the destination file.
+ #
+ local mode owner
+ mode=$( stat -f '%#Lp' "$file" 2> /dev/null )
+ owner=$( stat -f '%u:%g' "$file" 2> /dev/null )
+ f_quietly chmod "${mode:-0644}" "$tmpfile"
+ f_quietly chown "${owner:-root:wheel}" "$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 ! /bin/sh -n "$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.
+ #
+ mv "$tmpfile" "$file"
+ done
+}
+
+fi # ! $_SYSRC_SUBR
OpenPOWER on IntegriCloud