summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bsdconfig/share
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bsdconfig/share')
-rw-r--r--usr.sbin/bsdconfig/share/Makefile10
-rw-r--r--usr.sbin/bsdconfig/share/Makefile.depend11
-rw-r--r--usr.sbin/bsdconfig/share/common.subr1047
-rw-r--r--usr.sbin/bsdconfig/share/device.subr1396
-rw-r--r--usr.sbin/bsdconfig/share/dialog.subr2341
-rw-r--r--usr.sbin/bsdconfig/share/geom.subr430
-rw-r--r--usr.sbin/bsdconfig/share/keymap.subr263
-rw-r--r--usr.sbin/bsdconfig/share/media/Makefile8
-rw-r--r--usr.sbin/bsdconfig/share/media/Makefile.depend11
-rw-r--r--usr.sbin/bsdconfig/share/media/any.subr149
-rw-r--r--usr.sbin/bsdconfig/share/media/cdrom.subr217
-rw-r--r--usr.sbin/bsdconfig/share/media/common.subr155
-rw-r--r--usr.sbin/bsdconfig/share/media/directory.subr151
-rw-r--r--usr.sbin/bsdconfig/share/media/dos.subr165
-rw-r--r--usr.sbin/bsdconfig/share/media/floppy.subr229
-rw-r--r--usr.sbin/bsdconfig/share/media/ftp.subr911
-rw-r--r--usr.sbin/bsdconfig/share/media/http.subr688
-rw-r--r--usr.sbin/bsdconfig/share/media/httpproxy.subr463
-rw-r--r--usr.sbin/bsdconfig/share/media/network.subr182
-rw-r--r--usr.sbin/bsdconfig/share/media/nfs.subr258
-rw-r--r--usr.sbin/bsdconfig/share/media/options.subr327
-rw-r--r--usr.sbin/bsdconfig/share/media/tcpip.subr1713
-rw-r--r--usr.sbin/bsdconfig/share/media/ufs.subr198
-rw-r--r--usr.sbin/bsdconfig/share/media/usb.subr176
-rw-r--r--usr.sbin/bsdconfig/share/mustberoot.subr424
-rw-r--r--usr.sbin/bsdconfig/share/packages/Makefile6
-rw-r--r--usr.sbin/bsdconfig/share/packages/Makefile.depend11
-rw-r--r--usr.sbin/bsdconfig/share/packages/categories.subr209
-rw-r--r--usr.sbin/bsdconfig/share/packages/index.subr414
-rw-r--r--usr.sbin/bsdconfig/share/packages/musthavepkg.subr87
-rw-r--r--usr.sbin/bsdconfig/share/packages/packages.subr1192
-rw-r--r--usr.sbin/bsdconfig/share/script.subr219
-rw-r--r--usr.sbin/bsdconfig/share/strings.subr454
-rw-r--r--usr.sbin/bsdconfig/share/struct.subr206
-rw-r--r--usr.sbin/bsdconfig/share/sysrc.subr746
-rw-r--r--usr.sbin/bsdconfig/share/variable.subr315
36 files changed, 15782 insertions, 0 deletions
diff --git a/usr.sbin/bsdconfig/share/Makefile b/usr.sbin/bsdconfig/share/Makefile
new file mode 100644
index 0000000..a09697a
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+SUBDIR= media packages
+
+FILESDIR= ${SHAREDIR}/bsdconfig
+FILES= common.subr device.subr dialog.subr geom.subr keymap.subr \
+ mustberoot.subr script.subr strings.subr struct.subr \
+ sysrc.subr variable.subr
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsdconfig/share/Makefile.depend b/usr.sbin/bsdconfig/share/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr
new file mode 100644
index 0000000..5996446
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/common.subr
@@ -0,0 +1,1047 @@
+if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1
+#
+# Copyright (c) 2012 Ron McDowell
+# Copyright (c) 2012-2015 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ CONFIGURATION
+
+#
+# Default file descriptors to link to stdout/stderr for passthru allowing
+# redirection within a sub-shell to bypass directly to the terminal.
+#
+: ${TERMINAL_STDOUT_PASSTHRU:=3}
+: ${TERMINAL_STDERR_PASSTHRU:=4}
+
+############################################################ GLOBALS
+
+#
+# Program name
+#
+pgm="${0##*/}"
+
+#
+# Program arguments
+#
+ARGC="$#"
+ARGV="$@"
+
+#
+# Global exit status variables
+#
+SUCCESS=0
+FAILURE=1
+
+#
+# Operating environment details
+#
+export UNAME_S="$( uname -s )" # Operating System (i.e. FreeBSD)
+export UNAME_P="$( uname -p )" # Processor Architecture (i.e. i386)
+export UNAME_M="$( uname -m )" # Machine platform (i.e. i386)
+export UNAME_R="$( uname -r )" # Release Level (i.e. X.Y-RELEASE)
+
+#
+# Default behavior is to call f_debug_init() automatically when loaded.
+#
+: ${DEBUG_SELF_INITIALIZE=1}
+
+#
+# Default behavior of f_debug_init() is to truncate $debugFile (set to NULL to
+# disable truncating the debug file when initializing). To get child processes
+# to append to the same log file, export this variarable (with a NULL value)
+# and also export debugFile with the desired value.
+#
+: ${DEBUG_INITIALIZE_FILE=1}
+
+#
+# Define standard optstring arguments that should be supported by all programs
+# using this include (unless DEBUG_SELF_INITIALIZE is set to NULL to prevent
+# f_debug_init() from autamatically processing "$@" for the below arguments):
+#
+# d Sets $debug to 1
+# D: Sets $debugFile to $OPTARG
+#
+GETOPTS_STDARGS="dD:"
+
+#
+# The getopts builtin will return 1 either when the end of "$@" or the first
+# invalid flag is reached. This makes it impossible to determine if you've
+# processed all the arguments or simply have hit an invalid flag. In the cases
+# where we want to tolerate invalid flags (f_debug_init() for example), the
+# following variable can be appended to your optstring argument to getopts,
+# preventing it from prematurely returning 1 before the end of the arguments.
+#
+# NOTE: This assumes that all unknown flags are argument-less.
+#
+GETOPTS_ALLFLAGS="abcdefghijklmnopqrstuvwxyz"
+GETOPTS_ALLFLAGS="${GETOPTS_ALLFLAGS}ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+GETOPTS_ALLFLAGS="${GETOPTS_ALLFLAGS}0123456789"
+
+#
+# When we get included, f_debug_init() will fire (unless $DEBUG_SELF_INITIALIZE
+# is set to disable automatic initialization) and process "$@" for a few global
+# options such as `-d' and/or `-D file'. However, if your program takes custom
+# flags that take arguments, this automatic processing may fail unexpectedly.
+#
+# The solution to this problem is to pre-define (before including this file)
+# the following variable (which defaults to NULL) to indicate that there are
+# extra flags that should be considered when performing automatic processing of
+# globally persistent flags.
+#
+: ${GETOPTS_EXTRA:=}
+
+############################################################ FUNCTIONS
+
+# f_dprintf $format [$arguments ...]
+#
+# Sensible debug function. Override in ~/.bsdconfigrc if desired.
+# See /usr/share/examples/bsdconfig/bsdconfigrc for example.
+#
+# If $debug is set and non-NULL, prints DEBUG info using printf(1) syntax:
+# + To $debugFile, if set and non-NULL
+# + To standard output if $debugFile is either NULL or unset
+# + To both if $debugFile begins with a single plus-sign (`+')
+#
+f_dprintf()
+{
+ [ "$debug" ] || return $SUCCESS
+ local fmt="$1"; shift
+ case "$debugFile" in ""|+*)
+ printf "DEBUG: $fmt${fmt:+\n}" "$@" >&${TERMINAL_STDOUT_PASSTHRU:-1}
+ esac
+ [ "${debugFile#+}" ] &&
+ printf "DEBUG: $fmt${fmt:+\n}" "$@" >> "${debugFile#+}"
+ return $SUCCESS
+}
+
+# f_debug_init
+#
+# Initialize debugging. Truncates $debugFile to zero bytes if set.
+#
+f_debug_init()
+{
+ #
+ # Process stored command-line arguments
+ #
+ set -- $ARGV
+ local OPTIND OPTARG flag
+ f_dprintf "f_debug_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \
+ "$ARGV" "$GETOPTS_STDARGS"
+ while getopts "$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" flag \
+ > /dev/null; do
+ case "$flag" in
+ d) debug=1 ;;
+ D) debugFile="$OPTARG" ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+ f_dprintf "f_debug_init: debug=[%s] debugFile=[%s]" \
+ "$debug" "$debugFile"
+
+ #
+ # Automagically enable debugging if debugFile is set (and non-NULL)
+ #
+ [ "$debugFile" ] && { [ "${debug+set}" ] || debug=1; }
+
+ #
+ # Make debugging persistant if set
+ #
+ [ "$debug" ] && export debug
+ [ "$debugFile" ] && export debugFile
+
+ #
+ # Truncate debug file unless requested otherwise. Note that we will
+ # trim a leading plus (`+') from the value of debugFile to support
+ # persistant meaning that f_dprintf() should print both to standard
+ # output and $debugFile (minus the leading plus, of course).
+ #
+ local _debug_file="${debugFile#+}"
+ if [ "$_debug_file" -a "$DEBUG_INITIALIZE_FILE" ]; then
+ if ( umask 022 && :> "$_debug_file" ); then
+ f_dprintf "Successfully initialized debugFile \`%s'" \
+ "$_debug_file"
+ f_isset debug || debug=1 # turn debugging on if not set
+ else
+ unset debugFile
+ f_dprintf "Unable to initialize debugFile \`%s'" \
+ "$_debug_file"
+ fi
+ fi
+}
+
+# f_err $format [$arguments ...]
+#
+# 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 "$@"
+}
+
+# setvar $var_to_set [$value]
+#
+# Implement setvar for shells unlike FreeBSD sh(1).
+#
+if ! f_have setvar; then
+setvar()
+{
+ [ $# -gt 0 ] || return $SUCCESS
+ local __setvar_var_to_set="$1" __setvar_right="$2" __setvar_left=
+ case $# in
+ 1) unset "$__setvar_var_to_set"
+ return $? ;;
+ 2) : fall through ;;
+ *) f_err "setvar: too many arguments\n"
+ return $FAILURE
+ esac
+ case "$__setvar_var_to_set" in *[!0-9A-Za-z_]*)
+ f_err "setvar: %s: bad variable name\n" "$__setvar_var_to_set"
+ return 2
+ esac
+ while case "$__setvar_r" in *\'*) : ;; *) false ; esac
+ do
+ __setvar_left="$__setvar_left${__setvar_right%%\'*}'\\''"
+ __setvar_right="${__setvar_right#*\'}"
+ done
+ __setvar_left="$__setvar_left${__setvar_right#*\'}"
+ eval "$__setvar_var_to_set='$__setvar_left'"
+}
+fi
+
+# f_which $anything [$var_to_set]
+#
+# A fast built-in replacement for syntaxes such as foo=$( which bar ). In a
+# comparison of 10,000 runs of this function versus which, this function
+# completed in under 3 seconds, while `which' took almost a full minute.
+#
+# If $var_to_set is missing or NULL, output is (like which) to standard out.
+# Returns success if a match was found, failure otherwise.
+#
+f_which()
+{
+ local __name="$1" __var_to_set="$2"
+ case "$__name" in */*|'') return $FAILURE; esac
+ local __p __exec IFS=":" __found=
+ for __p in $PATH; do
+ __exec="$__p/$__name"
+ [ -f "$__exec" -a -x "$__exec" ] && __found=1 break
+ done
+ if [ "$__found" ]; then
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__exec"
+ else
+ echo "$__exec"
+ fi
+ return $SUCCESS
+ fi
+ return $FAILURE
+}
+
+# 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_get+set}\" ]
+ local __retval=$?
+ eval ${__var_to_set:-value}=\"\${$__var_to_get}\"
+ 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_isset $var
+#
+# Check if variable $var is set. Returns success if variable is set, otherwise
+# returns failure.
+#
+f_isset()
+{
+ eval [ \"\${${1%%[$IFS]*}+set}\" ]
+}
+
+# f_die [$status [$format [$arguments ...]]]
+#
+# 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_info $format [$arguments ...]
+#
+# Display a message in a dialog infobox using printf(1) syntax.
+#
+f_show_info()
+{
+ local msg
+ msg=$( printf "$@" )
+
+ #
+ # Use f_dialog_infobox 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_info; then
+ f_dialog_info "$msg"
+ else
+ dialog --infobox "$msg" 0 0
+ fi
+}
+
+# f_show_msg $format [$arguments ...]
+#
+# 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_show_err $format [$arguments ...]
+#
+# Display a message in a dialog box with ``Error'' i18n title (overridden by
+# setting msg_error) using printf(1) syntax.
+#
+f_show_err()
+{
+ local msg
+ msg=$( printf "$@" )
+
+ : ${msg:=${msg_an_unknown_error_occurred:-An unknown error occurred}}
+
+ if [ "$_DIALOG_SUBR" ]; then
+ f_dialog_title "${msg_error:-Error}"
+ f_dialog_msgbox "$msg"
+ f_dialog_title_restore
+ else
+ dialog --title "${msg_error:-Error}" --msgbox "$msg" 0 0
+ fi
+ return $SUCCESS
+}
+
+# f_yesno $format [$arguments ...]
+#
+# Display a message in a dialog yes/no box using printf(1) syntax.
+#
+f_yesno()
+{
+ local msg
+ msg=$( printf "$@" )
+
+ #
+ # Use f_dialog_yesno 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_yesno; then
+ f_dialog_yesno "$msg"
+ else
+ dialog --yesno "$msg" 0 0
+ fi
+}
+
+# f_noyes $format [$arguments ...]
+#
+# Display a message in a dialog yes/no box using printf(1) syntax.
+# NOTE: THis is just like the f_yesno function except "No" is default.
+#
+f_noyes()
+{
+ local msg
+ msg=$( printf "$@" )
+
+ #
+ # Use f_dialog_noyes 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_noyes; then
+ f_dialog_noyes "$msg"
+ else
+ dialog --defaultno --yesno "$msg" 0 0
+ fi
+}
+
+# f_show_help $file
+#
+# Display a language help-file. Automatically takes $LANG and $LC_ALL into
+# consideration when displaying $file (suffix ".$LC_ALL" or ".$LANG" will
+# automatically be added prior to loading the language help-file).
+#
+# If a language has been requested by setting either $LANG or $LC_ALL in the
+# environment and the language-specific help-file does not exist we will fall
+# back to $file without-suffix.
+#
+# If the language help-file does not exist, an error is displayed instead.
+#
+f_show_help()
+{
+ local file="$1"
+ local lang="${LANG:-$LC_ALL}"
+
+ [ -f "$file.$lang" ] && file="$file.$lang"
+
+ #
+ # Use f_dialog_textbox 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_textbox; then
+ f_dialog_textbox "$file"
+ else
+ dialog --msgbox "$( cat "$file" 2>&1 )" 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"
+ f_dprintf "f_include: file=[%s]" "$file"
+ . "$file" || exit $?
+}
+
+# f_include_lang $file
+#
+# Include a language file. Automatically takes $LANG and $LC_ALL into
+# consideration 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 "f_include_lang: file=[%s] lang=[%s]" "$file" "$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 being 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 "f_usage: file=[%s] lang=[%s]" "$file" "$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_file $keyword [$var_to_set]
+#
+# Process all INDEX files known to bsdconfig and return the path to first file
+# containing a menu_selection line with a keyword portion matching $keyword.
+#
+# If $LANG or $LC_ALL (in order of preference, respectively) is set,
+# "INDEX.encoding" files will be searched first.
+#
+# If no file is found, error status is returned along with the NULL string.
+#
+# If $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+# 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_file_awk='
+# Variables that should be defined on the invocation line:
+# -v keyword="keyword"
+BEGIN { found = 0 }
+( $0 ~ "^menu_selection=\"" keyword "\\|" ) {
+ print FILENAME
+ found++
+ exit
+}
+END { exit ! found }
+'
+f_index_file()
+{
+ local __keyword="$1" __var_to_set="$2"
+ local __lang="${LANG:-$LC_ALL}"
+ local __indexes="$BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX"
+
+ f_dprintf "f_index_file: keyword=[%s] lang=[%s]" "$__keyword" "$__lang"
+
+ if [ "$__lang" ]; then
+ if [ "$__var_to_set" ]; then
+ eval "$__var_to_set"='"$( awk -v keyword="$__keyword" \
+ "$f_index_file_awk" $__indexes.$__lang
+ )"' && return $SUCCESS
+ else
+ awk -v keyword="$__keyword" "$f_index_file_awk" \
+ $__indexes.$__lang && return $SUCCESS
+ fi
+ # No match, fall-thru to non-i18n sources
+ fi
+ if [ "$__var_to_set" ]; then
+ eval "$__var_to_set"='"$( awk -v keyword="$__keyword" \
+ "$f_index_file_awk" $__indexes )"' && return $SUCCESS
+ else
+ awk -v keyword="$__keyword" "$f_index_file_awk" $__indexes &&
+ return $SUCCESS
+ fi
+
+ # No match? Fall-thru to `local' libexec sources (add-on modules)
+
+ [ "$BSDCFG_LOCAL_LIBE" ] || return $FAILURE
+ __indexes="$BSDCFG_LOCAL_LIBE/*/INDEX"
+ if [ "$__lang" ]; then
+ if [ "$__var_to_set" ]; then
+ eval "$__var_to_set"='"$( awk -v keyword="$__keyword" \
+ "$f_index_file_awk" $__indexes.$__lang
+ )"' && return $SUCCESS
+ else
+ awk -v keyword="$__keyword" "$f_index_file_awk" \
+ $__indexes.$__lang && return $SUCCESS
+ fi
+ # No match, fall-thru to non-i18n sources
+ fi
+ if [ "$__var_to_set" ]; then
+ eval "$__var_to_set"='$( awk -v keyword="$__keyword" \
+ "$f_index_file_awk" $__indexes )"'
+ else
+ awk -v keyword="$__keyword" "$f_index_file_awk" $__indexes
+ fi
+}
+
+# f_index_menusel_keyword $indexfile $pgm [$var_to_set]
+#
+# Process $indexfile and return only the keyword portion of the menu_selection
+# line with a command portion matching $pgm.
+#
+# This function is for internationalization (i18n) mapping of the on-disk
+# scriptname ($pgm) into the localized language (given language-specific
+# $indexfile). If $LANG or $LC_ALL (in orderder of preference, respectively) is
+# set, ".encoding" will automatically be appended as a suffix to the provided
+# $indexfile pathname.
+#
+# If, within $indexfile, 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 $indexfile does not exist, error status is returned with NULL.
+#
+# If $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+# 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_keyword_awk='
+# Variables that should be defined on the invocation line:
+# -v pgm="program_name"
+#
+BEGIN {
+ prefix = "menu_selection=\""
+ plen = length(prefix)
+ found = 0
+}
+{
+ if (!match($0, "^" prefix ".*\\|.*\"")) next
+
+ keyword = command = substr($0, plen + 1, RLENGTH - plen - 1)
+ sub(/^.*\|/, "", command)
+ sub(/\|.*$/, "", keyword)
+
+ if ( command == pgm )
+ {
+ print keyword
+ found++
+ exit
+ }
+}
+END { exit ! found }
+'
+f_index_menusel_keyword()
+{
+ local __indexfile="$1" __pgm="$2" __var_to_set="$3"
+ local __lang="${LANG:-$LC_ALL}" __file="$__indexfile"
+
+ [ -f "$__indexfile.$__lang" ] && __file="$__indexfile.$__lang"
+ f_dprintf "f_index_menusel_keyword: index=[%s] pgm=[%s] lang=[%s]" \
+ "$__file" "$__pgm" "$__lang"
+
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$( awk \
+ -v pgm="$__pgm" "$f_index_menusel_keyword_awk" "$__file"
+ )"
+ else
+ awk -v pgm="$__pgm" "$f_index_menusel_keyword_awk" "$__file"
+ fi
+}
+
+# f_index_menusel_command $indexfile $keyword [$var_to_set]
+#
+# Process $indexfile and return only the command portion of the menu_selection
+# line with a keyword portion matching $keyword.
+#
+# This function is for mapping [possibly international] keywords into the
+# command to be executed. If $LANG or $LC_ALL (order of preference) is set,
+# ".encoding" will automatically be appended as a suffix to the provided
+# $indexfile pathname.
+#
+# If, within $indexfile, multiple $menu_selection values map to $keyword, only
+# the first one will be returned. If no mapping can be made, the NULL string is
+# returned.
+#
+# If $indexfile doesn't exist, error status is returned with NULL.
+#
+# If $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+# 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_command_awk='
+# Variables that should be defined on the invocation line:
+# -v key="keyword"
+#
+BEGIN {
+ prefix = "menu_selection=\""
+ plen = length(prefix)
+ found = 0
+}
+{
+ if (!match($0, "^" prefix ".*\\|.*\"")) next
+
+ keyword = command = substr($0, plen + 1, RLENGTH - plen - 1)
+ sub(/^.*\|/, "", command)
+ sub(/\|.*$/, "", keyword)
+
+ if ( keyword == key )
+ {
+ print command
+ found++
+ exit
+ }
+}
+END { exit ! found }
+'
+f_index_menusel_command()
+{
+ local __indexfile="$1" __keyword="$2" __var_to_set="$3" __command
+ local __lang="${LANG:-$LC_ALL}" __file="$__indexfile"
+
+ [ -f "$__indexfile.$__lang" ] && __file="$__indexfile.$__lang"
+ f_dprintf "f_index_menusel_command: index=[%s] key=[%s] lang=[%s]" \
+ "$__file" "$__keyword" "$__lang"
+
+ [ -f "$__file" ] || return $FAILURE
+ __command=$( awk -v key="$__keyword" \
+ "$f_index_menusel_command_awk" "$__file" ) || return $FAILURE
+
+ #
+ # If the command pathname is not fully qualified fix-up/force to be
+ # relative to the $indexfile directory.
+ #
+ case "$__command" in
+ /*) : already fully qualified ;;
+ *)
+ local __indexdir="${__indexfile%/*}"
+ [ "$__indexdir" != "$__indexfile" ] || __indexdir="."
+ __command="$__indexdir/$__command"
+ esac
+
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__command"
+ else
+ echo "$__command"
+ fi
+}
+
+# f_running_as_init
+#
+# Returns true if running as init(1).
+#
+f_running_as_init()
+{
+ #
+ # When a custom init(8) performs an exec(3) to invoke a shell script,
+ # PID 1 becomes sh(1) and $PPID is set to 1 in the executed script.
+ #
+ [ ${PPID:-0} -eq 1 ] # Return status
+}
+
+# f_mounted $local_directory
+# f_mounted -b $device
+#
+# Return success if a filesystem is mounted on a particular directory. If `-b'
+# is present, instead check that the block device (or a partition thereof) is
+# mounted.
+#
+f_mounted()
+{
+ local OPTIND OPTARG flag use_device=
+ while getopts b flag; do
+ case "$flag" in
+ b) use_device=1 ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+ if [ "$use_device" ]; then
+ local device="$1"
+ mount | grep -Eq \
+ "^$device([[:space:]]|p[0-9]|s[0-9]|\.nop|\.eli)"
+ else
+ [ -d "$dir" ] || return $FAILURE
+ mount | grep -Eq " on $dir \([^)]+\)$"
+ fi
+ # Return status is that of last grep(1)
+}
+
+# f_eval_catch [-de] [-k $var_to_set] $funcname $utility \
+# $format [$arguments ...]
+#
+# Silently evaluate a command in a sub-shell and test for error. If debugging
+# is enabled a copy of the command and its output is sent to debug (either
+# stdout or file depending on environment). If an error occurs, output of the
+# command is displayed in a dialog(1) msgbox using the [above] f_show_err()
+# function (unless optional `-d' flag is given, then no dialog).
+#
+# The $funcname argument is sent to debugging while the $utility argument is
+# used in the title of the dialog box. The command that is executed as well as
+# sent to debugging with $funcname is the product of the printf(1) syntax
+# produced by $format with optional $arguments.
+#
+# The following options are supported:
+#
+# -d Do not use dialog(1).
+# -e Produce error text from failed command on stderr.
+# -k var Save output from the command in var.
+#
+# Example 1:
+#
+# debug=1
+# f_eval_catch myfunc echo 'echo "%s"' "Hello, World!"
+#
+# Produces the following debug output:
+#
+# DEBUG: myfunc: echo "Hello, World!"
+# DEBUG: myfunc: retval=0 <output below>
+# Hello, World!
+#
+# Example 2:
+#
+# debug=1
+# f_eval_catch -k contents myfunc cat 'cat "%s"' /some/file
+# # dialog(1) Error ``cat: /some/file: No such file or directory''
+# # contents=[cat: /some/file: No such file or directory]
+#
+# Produces the following debug output:
+#
+# DEBUG: myfunc: cat "/some/file"
+# DEBUG: myfunc: retval=1 <output below>
+# cat: /some/file: No such file or directory
+#
+# Example 3:
+#
+# debug=1
+# echo 123 | f_eval_catch myfunc rev rev
+#
+# Produces the following debug output:
+#
+# DEBUG: myfunc: rev
+# DEBUG: myfunc: retval=0 <output below>
+# 321
+#
+# Example 4:
+#
+# debug=1
+# f_eval_catch myfunc true true
+#
+# Produces the following debug output:
+#
+# DEBUG: myfunc: true
+# DEBUG: myfunc: retval=0 <no output>
+#
+# Example 5:
+#
+# f_eval_catch -de myfunc ls 'ls "%s"' /some/dir
+# # Output on stderr ``ls: /some/dir: No such file or directory''
+#
+# Example 6:
+#
+# f_eval_catch -dek contents myfunc ls 'ls "%s"' /etc
+# # Output from `ls' sent to stderr and also saved in $contents
+#
+f_eval_catch()
+{
+ local __no_dialog= __show_err= __var_to_set=
+
+ #
+ # Process local function arguments
+ #
+ local OPTIND OPTARG __flag
+ while getopts "dek:" __flag > /dev/null; do
+ case "$__flag" in
+ d) __no_dialog=1 ;;
+ e) __show_err=1 ;;
+ k) __var_to_set="$OPTARG" ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ local __funcname="$1" __utility="$2"; shift 2
+ local __cmd __output __retval
+
+ __cmd=$( printf -- "$@" )
+ f_dprintf "%s: %s" "$__funcname" "$__cmd" # Log command *before* eval
+ __output=$( exec 2>&1; eval "$__cmd" )
+ __retval=$?
+ if [ "$__output" ]; then
+ [ "$__show_err" ] && echo "$__output" >&2
+ f_dprintf "%s: retval=%i <output below>\n%s" "$__funcname" \
+ $__retval "$__output"
+ else
+ f_dprintf "%s: retval=%i <no output>" "$__funcname" $__retval
+ fi
+
+ ! [ "$__no_dialog" -o "$nonInteractive" -o $__retval -eq $SUCCESS ] &&
+ msg_error="${msg_error:-Error}${__utility:+: $__utility}" \
+ f_show_err "%s" "$__output"
+ # NB: f_show_err will handle NULL output appropriately
+
+ [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output"
+
+ return $__retval
+}
+
+# f_count $var_to_set arguments ...
+#
+# Sets $var_to_set to the number of arguments minus one (the effective number
+# of arguments following $var_to_set).
+#
+# Example:
+# f_count count dog house # count=[2]
+#
+f_count()
+{
+ setvar "$1" $(( $# - 1 ))
+}
+
+# f_count_ifs $var_to_set string ...
+#
+# Sets $var_to_set to the number of words (split by the internal field
+# separator, IFS) following $var_to_set.
+#
+# Example 1:
+#
+# string="word1 word2 word3"
+# f_count_ifs count "$string" # count=[3]
+# f_count_ifs count $string # count=[3]
+#
+# Example 2:
+#
+# IFS=. f_count_ifs count www.freebsd.org # count=[3]
+#
+# NB: Make sure to use double-quotes if you are using a custom value for IFS
+# and you don't want the current value to effect the result. See example 3.
+#
+# Example 3:
+#
+# string="a-b c-d"
+# IFS=- f_count_ifs count "$string" # count=[3]
+# IFS=- f_count_ifs count $string # count=[4]
+#
+f_count_ifs()
+{
+ local __var_to_set="$1"
+ shift 1
+ set -- $*
+ setvar "$__var_to_set" $#
+}
+
+############################################################ 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
+
+#
+# Clone terminal stdout/stderr so we can redirect to it from within sub-shells
+#
+eval exec $TERMINAL_STDOUT_PASSTHRU\>\&1
+eval exec $TERMINAL_STDERR_PASSTHRU\>\&2
+
+#
+# Self-initialize unless requested otherwise
+#
+f_dprintf "%s: DEBUG_SELF_INITIALIZE=[%s]" \
+ dialog.subr "$DEBUG_SELF_INITIALIZE"
+case "$DEBUG_SELF_INITIALIZE" in
+""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
+*) f_debug_init
+esac
+
+#
+# Log our operating environment for debugging purposes
+#
+f_dprintf "UNAME_S=[%s] UNAME_P=[%s] UNAME_R=[%s]" \
+ "$UNAME_S" "$UNAME_P" "$UNAME_R"
+
+f_dprintf "%s: Successfully loaded." common.subr
+
+fi # ! $_COMMON_SUBR
diff --git a/usr.sbin/bsdconfig/share/device.subr b/usr.sbin/bsdconfig/share/device.subr
new file mode 100644
index 0000000..d95684d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/device.subr
@@ -0,0 +1,1396 @@
+if [ ! "$_DEVICE_SUBR" ]; then _DEVICE_SUBR=1
+#
+# Copyright (c) 2012-2014 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/geom.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/struct.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+NDEVICES=0 # Set by f_device_register(), used by f_device_*()
+
+#
+# A "device" from legacy sysinstall's point of view (mostly)
+#
+# NB: Disk devices have their `private' property set to GEOM ident
+# NB: USB devices have their `private' property set to USB disk device name
+#
+f_struct_define DEVICE \
+ capacity \
+ desc \
+ devname \
+ enabled \
+ flags \
+ get \
+ init \
+ name \
+ private \
+ shutdown \
+ type \
+ volume
+
+# Network devices have their `private' property set to this
+f_struct_define DEVICE_INFO \
+ extras \
+ ipaddr \
+ ipv6addr \
+ netmask \
+ use_dhcp \
+ use_rtsol
+
+#
+# Device types for f_device_register(), f_device_find(), et al.
+#
+setvar DEVICE_TYPE_ANY "any" # Any
+setvar DEVICE_TYPE_NONE "NONE" # Unknown
+setvar DEVICE_TYPE_DISK "DISK" # GEOM `DISK'
+setvar DEVICE_TYPE_FLOPPY "FD" # GEOM `FD'
+setvar DEVICE_TYPE_FTP "FTP" # Dynamic network device
+setvar DEVICE_TYPE_NETWORK "NETWORK" # See f_device_get_all_network
+setvar DEVICE_TYPE_CDROM "CDROM" # GEOM `DISK'
+setvar DEVICE_TYPE_USB "USB" # GEOM `PART'
+setvar DEVICE_TYPE_DOS "DOS" # GEOM `DISK' `PART' or `LABEL'
+setvar DEVICE_TYPE_UFS "UFS" # GEOM `DISK' `PART' or `LABEL'
+setvar DEVICE_TYPE_NFS "NFS" # Dynamic network device
+setvar DEVICE_TYPE_HTTP_PROXY "HTTP_PROXY" # Dynamic network device
+setvar DEVICE_TYPE_HTTP "HTTP" # Dynamic network device
+
+# Network devices have the following flags available
+setvar IF_ETHERNET 1
+setvar IF_WIRELESS 2
+setvar IF_ACTIVE 4
+
+#
+# Default behavior is to call f_device_get_all() automatically when loaded.
+#
+: ${DEVICE_SELF_SCAN_ALL=1}
+
+#
+# Device Catalog variables
+#
+DEVICE_CATALOG_APPEND_ONLY= # Used by f_device_catalog_set()
+NCATALOG_DEVICES=0 # Used by f_device_catalog_*() and MAIN
+
+#
+# A ``catalog'' device is for mapping GEOM devices to media devices (for
+# example, determining if a $GEOM_CLASS_DISK geom is $DEVICE_TYPE_CDROM or
+# $DEVICE_TYPE_DISK) and also getting default descriptions for devices that
+# either lack a GEOM provided description or lack a presence in GEOM)
+#
+f_struct_define CATALOG_DEVICE \
+ desc \
+ name \
+ type
+
+############################################################ FUNCTIONS
+
+# f_device_register $var_to_set $name $desc $devname $type $enabled
+# $init_function $get_function $shutdown_function
+# $private $capacity
+#
+# Register a device. A `structure' (see struct.subr) is created and if
+# $var_to_set is non-NULL, upon success holds the name of the struct created.
+# The remaining positional arguments correspond to the properties of the
+# `DEVICE' structure-type to be assigned (defined above).
+#
+# If not already registered (based on $name and $type), a new device is created
+# and $NDEVICES is incremented.
+#
+f_device_register()
+{
+ local __var_to_set="$1" __name="$2" __desc="$3" __devname="$4"
+ local __type="$5" __enabled="$6" __init_func="$7" __get_func="$8"
+ local __shutdown_func="$9" __private="${10}" __capacity="${11}"
+
+ # Required parameter(s)
+ [ "$__name" ] || return $FAILURE
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "" || return $FAILURE
+ fi
+
+ local __device
+ if f_device_find -1 "$__name" "$__type" __device; then
+ f_struct_free "$__device"
+ f_struct_new DEVICE "$__device" || return $FAILURE
+ else
+ __device=device_$(( NDEVICES + 1 ))
+ f_struct_new DEVICE "$__device" || return $FAILURE
+ NDEVICES=$(( $NDEVICES + 1 ))
+ fi
+ $__device set name "$__name"
+ $__device set desc "$__desc"
+ $__device set devname "$__devname"
+ $__device set type "$__type"
+ $__device set enabled "$__enabled"
+ $__device set init "$__init_func"
+ $__device set get "$__get_func"
+ $__device set shutdown "$__shutdown_func"
+ $__device set private "$__private"
+ $__device set capacity "$__capacity"
+
+ [ "$__var_to_set" ] && setvar "$__var_to_set" "$__device"
+ return $SUCCESS
+}
+
+# f_device_reset
+#
+# Reset the registered device chain.
+#
+f_device_reset()
+{
+ local n=1
+ while [ $n -le $NDEVICES ]; do
+ f_device_shutdown device_$n
+
+ #
+ # XXX This potentially leaks $dev->private if it's being
+ # used to point to something dynamic, but you're not supposed
+ # to call this routine at such times that some open instance
+ # has its private member pointing somewhere anyway.
+ #
+ f_struct_free device_$n
+
+ n=$(( $n + 1 ))
+ done
+ NDEVICES=0
+}
+
+# f_device_reset_network
+#
+# Reset the registered network device chain.
+#
+f_device_reset_network()
+{
+ local n=1 device type private i
+ while [ $n -le $NDEVICES ]; do
+ device=device_$n
+ f_struct $device || continue
+ $device get type type
+ [ "$type" = "$DEVICE_TYPE_NETWORK" ] || continue
+
+ #
+ # Leave the device up (don't call shutdown routine)
+ #
+
+ # Network devices may have DEVICE_INFO private member
+ $device get private private
+ [ "$private" ] && f_struct_free "$private"
+
+ # Free the network device
+ f_struct_free $device
+
+ # Fill the gap we just created
+ i=$n
+ while [ $i -lt $NDEVICES ]; do
+ f_struct_copy device_$(( $i + 1 )) device_$i
+ done
+ f_struct_free device_$NDEVICES
+
+ # Finally decrement the number of devices
+ NDEVICES=$(( $NDEVICES - 1 ))
+
+ n=$(( $n + 1 ))
+ done
+}
+
+# f_device_get_all
+#
+# Get all device information for all devices.
+#
+f_device_get_all()
+{
+ local devname type desc capacity
+
+ f_dprintf "f_device_get_all: Probing devices..."
+ f_dialog_info "$msg_probing_devices_please_wait_this_can_take_a_while"
+
+ # First go for the network interfaces
+ f_device_get_all_network
+
+ # Next, go for the GEOM devices we might want to use as media
+ local geom geoms geom_name
+ debug= f_geom_find "" $GEOM_CLASS_DEV geoms
+ for geom in $geoms; do
+ if ! f_device_probe_geom $geom; then
+ debug= $geom get name geom_name
+ f_dprintf "WARNING! Unable to classify %s as %s" \
+ "GEOM device $geom_name" "media source"
+ fi
+ done
+}
+
+# f_device_get_all_network
+#
+# Get all network device information for attached network devices.
+#
+f_device_get_all_network()
+{
+ local devname desc device flags
+ for devname in $( ifconfig -l ); do
+ # Eliminate network devices that don't make sense
+ case "$devname" in
+ lo*) continue ;;
+ esac
+
+ # Try and find its description
+ f_device_desc "$devname" $DEVICE_TYPE_NETWORK desc
+
+ f_dprintf "Found network device named %s" "$devname"
+ debug= f_device_register device $devname "$desc" \
+ "$devname" $DEVICE_TYPE_NETWORK 1 \
+ f_media_init_network "" f_media_shutdown_network "" -1
+
+ # Set flags based on media and status
+ flags=0
+ eval "$( ifconfig $devname 2> /dev/null | awk -v var=flags '
+ function _or(var, mask) {
+ printf "%s=$(( $%s | $%s ))\n", var, var, mask
+ }
+ BEGIN { S = "[[:space:]]+" }
+ {
+ if (!match($0, "^" S "(media|status):" S)) next
+ value = substr($0, RLENGTH + 1)
+ if ($1 == "media:") {
+ if (value ~ /Ethernet/) _or(var, "IF_ETHERNET")
+ if (value ~ /802\.11/) _or(var, "IF_WIRELESS")
+ } else if ($1 == "status:") {
+ if (value ~ /^active/) _or(var, "IF_ACTIVE")
+ }
+ }' )"
+ $device set flags $flags
+ done
+}
+
+# f_device_rescan
+#
+# Rescan all devices, after closing previous set - convenience function.
+#
+f_device_rescan()
+{
+ f_device_reset
+ f_geom_rescan
+ f_device_get_all
+}
+
+# f_device_rescan_network
+#
+# Rescan all network devices, after closing previous set - for convenience.
+#
+f_device_rescan_network()
+{
+ f_device_reset_network
+ f_device_get_all_network
+}
+
+# f_device_probe_geom $geom
+#
+# Probe a single GEOM device and if it can be classified as a media source,
+# register it using f_device_register() with known type-specific arguments.
+#
+f_device_probe_geom()
+{
+ local geom="$1"
+
+ f_struct "$geom" || return $FAILURE
+
+ # geom associated variables
+ local geom_name geom_consumer provider_ref geom_provider=
+ local provider_geom provider_config provider_class=
+ local provider_config_type catalog_struct catalog_type
+ local disk_ident
+
+ # gnop(8)/geli(8) associated variables (p for `parent device')
+ local p_devname p_geom p_consumer p_provider_ref p_provider
+ local p_provider_config p_provider_geom p_provider_class
+
+ # md(4) associated variables
+ local config config_type config_file magic=
+
+ # Temporarily disable debugging to keep debug output light
+ local old_debug="$debug" debug=
+
+ #
+ # Get the GEOM name (for use below in device registration)
+ #
+ $geom get name devname || continue
+
+ #
+ # Attempt to get the consumer, provider, provider config, and
+ # provider class for this geom (errors ignored).
+ #
+ # NB: Each GEOM in the `DEV' class should have one consumer.
+ # That consumer should have a reference to its provider.
+ #
+ $geom get consumer1 geom_consumer
+ f_struct "$geom_consumer" get provider_ref provider_ref &&
+ f_geom_find_by id "$provider_ref" provider geom_provider
+ if f_struct "$geom_provider"; then
+ $geom_provider get config provider_config
+ f_geom_parent $geom_provider provider_geom &&
+ f_geom_parent $provider_geom provider_class
+ fi
+
+ #
+ # Get values for device registration (errors ignored)
+ #
+ f_struct "$provider_class" get name type
+ f_struct "$geom_provider" get mediasize capacity
+ f_struct "$provider_config" get descr desc
+
+ #
+ # For gnop(8), geli(8), or combination thereof, change device type to
+ # that of the consumer
+ #
+ p_devname= p_geom= p_provider= p_provider_config=
+ case "$devname" in
+ *.nop.eli) p_devname="${devname%.nop.eli}" ;;
+ *.eli.nop) p_devname="${devname%.eli.nop}" ;;
+ *.eli) p_devname="${devname%.eli}" ;;
+ *.nop) p_devname="${devname%.nop}" ;;
+ esac
+ [ "$p_devname" ] && f_geom_find "$p_devname" $GEOM_CLASS_DEV p_geom
+ if [ "${p_geom:-$geom}" != "$geom" ]; then
+ f_struct "$p_geom" get consumer1 p_consumer
+ f_struct "$p_consumer" get provider_ref p_provider_ref &&
+ f_geom_find_by id "$p_provider_ref" provider p_provider
+ if f_struct "$p_provider"; then
+ $p_provider get config p_provider_config
+ f_geom_parent $p_provider p_provider_geom &&
+ f_geom_parent $p_provider_geom p_provider_class
+ fi
+ f_struct "$p_provider_class" get name type
+ fi
+
+ # Look up geom device in device catalog for default description
+ f_device_catalog_get \
+ $DEVICE_TYPE_ANY "${p_devname:-$devname}" catalog_struct
+ [ "$desc" ] || f_struct "catalog_device_$catalog_struct" get desc desc
+
+ # Use device catalog entry for potential re-classification(s)
+ f_struct "catalog_device_$catalog_struct" get type catalog_type
+
+ # Restore debugging for this next part (device registration)
+ debug="$old_debug"
+
+ #
+ # Register the device
+ #
+ local retval device
+ case "$type" in
+ $GEOM_CLASS_DISK)
+ # First attempt to classify by device catalog (see MAIN)
+ case "$catalog_type" in
+ $DEVICE_TYPE_CDROM)
+ f_dprintf "Found CDROM device for disk %s" "$devname"
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_CDROM 1 \
+ f_media_init_cdrom f_media_get_cdrom \
+ f_media_shutdown_cdrom "" "$capacity" &&
+ return $SUCCESS
+ ;;
+ esac
+
+ # Fall back to register label device as a disk and taste it
+ f_dprintf "Found disk device named %s" "$devname"
+ debug= f_struct "$p_provider_config" get \
+ ident disk_ident ||
+ debug= f_struct "$provider_config" get \
+ ident disk_ident
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_DISK 1 \
+ "" "" "" "$disk_ident" "$capacity"
+ retval=$?
+
+ # Detect ``dangerously dedicated'' filesystems (errors ignored)
+ f_device_probe_disk_fs device "$devname" "$capacity" &&
+ retval=$SUCCESS
+
+ return $retval
+ ;;
+ $GEOM_CLASS_FD)
+ f_dprintf "Found floppy device named %s" "$devname"
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_FLOPPY 1 \
+ f_media_init_floppy f_media_get_floppy \
+ f_media_shutdown_floppy "" "$capacity"
+ return $?
+ ;;
+ $GEOM_CLASS_LABEL)
+ : fall through to below section # reduces indentation level
+ ;;
+ $GEOM_CLASS_MD)
+ f_dprintf "Found disk device named %s" "$devname"
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_DISK 1 \
+ "" "" "" "" "$capacity"
+ retval=$?
+
+ #
+ # Attempt to get file(1) magic to potentially classify as
+ # alternate media type. If unable to get magic, fall back to
+ # md(4) characteristics (such as vnode filename).
+ #
+ [ -r "/dev/$devname" ] &&
+ magic=$( file -bs "/dev/$devname" 2> /dev/null )
+ if [ ! "$magic" ]; then
+ # Fall back to md(4) characteristics
+ if f_struct "$p_provider_config"; then
+ config="$p_provider_config"
+ else
+ config="$provider_config"
+ fi
+ debug= f_struct "$config" get type config_type
+ debug= f_struct "$config" get file config_file
+
+ # Substitute magic for below based on type and file
+ case "$config_type=$config_file" in
+ vnode=*.iso) magic="ISO 9660" ;;
+ esac
+ fi
+ f_device_probe_disk_fs device \
+ "$devname" "$capacity" "$magic" &&
+ retval=$SUCCESS # Errors ignored
+
+ return $retval
+ ;;
+ $GEOM_CLASS_PART)
+ if f_struct "$p_provider_config"; then
+ config="$p_provider_config"
+ else
+ config="$provider_config"
+ fi
+ debug= f_struct "$config" get type provider_config_type
+ f_device_probe_geom_part device \
+ "$provider_config_type" "$devname" "$capacity"
+ retval=$?
+ device_type=$DEVICE_TYPE_NONE
+ [ $retval -eq $SUCCESS ] &&
+ debug= f_struct "$device" get type device_type
+
+ # Potentially re-classify as USB device
+ if [ "$device_type" = "$DEVICE_TYPE_UFS" -a \
+ "$catalog_type" = "$DEVICE_TYPE_USB" ]
+ then
+ f_dprintf "Found USB device for partition %s" \
+ "$devname"
+ debug= f_struct "$p_provider_geom" get \
+ name disk_name ||
+ debug= f_struct "$provider_geom" get \
+ name disk_name
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_USB 1 \
+ f_media_init_usb f_media_get_usb \
+ f_media_shutdown_usb "$disk_name" "$capacity"
+ retval=$?
+ fi
+
+ return $retval
+ ;;
+ $GEOM_CLASS_RAID)
+ # Use the provider geom name as the description
+ if [ ! "$desc" ]; then
+ f_struct "$p_provider_geom" get name desc ||
+ f_struct "$provider_geom" get name desc
+ fi
+
+ f_dprintf "Found disk device named %s" "$devname"
+ debug= f_device_register device \
+ "$devname" "${desc:-GEOM RAID device}" \
+ "/dev/$devname" $DEVICE_TYPE_DISK 1 \
+ "" "" "" "" "$capacity"
+ retval=$?
+
+ # Detect ``dangerously dedicated'' filesystems
+ f_device_probe_disk_fs device "$devname" "$capacity" &&
+ retval=$SUCCESS # Errors ignored
+
+ return $retval
+ ;;
+ $GEOM_CLASS_ZFS_ZVOL)
+ f_dprintf "Found disk device named %s" "$devname"
+ debug= f_device_register device \
+ "$devname" "${desc:-GEOM ZFS::ZVOL device}" \
+ "/dev/$devname" $DEVICE_TYPE_DISK 1 \
+ "" "" "" "" "$capacity"
+ retval=$?
+
+ # Detect ``dangerously dedicated'' filesystems
+ f_device_probe_disk_fs device "$devname" "$capacity" &&
+ retval=$SUCCESS # Errors ignored
+
+ return $retval
+ ;;
+ *)
+ return $FAILURE # Unknown GEOM class
+ esac
+
+ #
+ # Still here? Must be $GEOM_CLASS_LABEL
+ #
+
+ local label_geom label_devname label_devgeom= label_devconsumer
+ local label_devprovider= label_devprovider_ref label_devprovider_config
+ local label_gpart_type
+
+ if f_struct "$p_provider"; then
+ label_geom="$p_provider_geom"
+ else
+ label_geom="$provider_geom"
+ fi
+
+ case "$devname" in
+ gpt/*|gptid/*)
+ #
+ # Attempt to get the partition type by getting the `config'
+ # member of the provider for our device (which is named in the
+ # parent geom of our current provider).
+ #
+ debug= f_struct "$label_geom" get name label_devname &&
+ debug= f_geom_find "$label_devname" $GEOM_CLASS_DEV \
+ label_devgeom
+ debug= f_struct "$label_devgeom" get \
+ consumer1 label_devconsumer
+ debug= f_struct "$label_devconsumer" get \
+ provider_ref label_devprovider_ref &&
+ debug= f_geom_find_by id "$label_devprovider_ref" \
+ provider label_devprovider
+ debug= f_struct "$label_devprovider" get \
+ config label_devprovider_config
+ debug= f_struct "$label_devprovider_config" get \
+ type label_gpart_type
+
+ #
+ # Register device label based on partition type
+ #
+ f_device_probe_geom_part device \
+ "$label_gpart_type" "$devname" "$capacity"
+ return $?
+ ;;
+ iso9660/*)
+ f_dprintf "Found CDROM device labeled %s" "$devname"
+ debug= f_device_register device \
+ "$devname" "ISO9660 file system" \
+ "/dev/$devname" $DEVICE_TYPE_CDROM 1 \
+ f_media_init_cdrom f_media_get_cdrom \
+ f_media_shutdown_cdrom "" "$capacity"
+ return $?
+ ;;
+ label/*)
+ # For generic labels, use provider geom name as real device
+ debug= f_struct "$label_geom" get name label_devname
+
+ # Look up label geom device in device catalog for default desc
+ debug= f_device_catalog_get \
+ $DEVICE_TYPE_ANY "$label_devname" catalog_struct
+ [ "$desc" ] || debug= f_struct \
+ "catalog_device_$catalog_struct" get desc desc
+
+ # Use device catalog entry for potential re-classification(s)
+ debug= f_struct "catalog_device_$catalog_struct" get \
+ type catalog_type
+
+ # First attempt to classify by device catalog (see MAIN)
+ case "$catalog_type" in
+ $DEVICE_TYPE_CDROM)
+ f_dprintf "Found CDROM device for disk %s" "$devname"
+ debug= f_device_register device "$devname" "$desc" \
+ "/dev/$devname" $DEVICE_TYPE_CDROM 1 \
+ f_media_init_cdrom f_media_get_cdrom \
+ f_media_shutdown_cdrom "" "$capacity" &&
+ return $SUCCESS
+ ;;
+ esac
+
+ # Fall back to register label device as a disk and taste it
+ f_dprintf "Found disk device labeled %s" "$devname"
+ debug= f_device_register device \
+ "$devname" "GEOM LABEL device" \
+ "/dev/$devname" $DEVICE_TYPE_DISK 1 \
+ "" "" "" "" "$capacity"
+ retval=$?
+
+ # Detect ``dangerously dedicated'' filesystems (errors ignored)
+ f_device_probe_disk_fs device "$devname" "$capacity" &&
+ retval=$SUCCESS
+
+ return $retval
+ ;;
+ msdosfs/*)
+ f_dprintf "Found DOS partition labeled %s" "$devname"
+ debug= f_device_register device "$devname" "DOS file system" \
+ "/dev/$devname" $DEVICE_TYPE_DOS 1 \
+ f_media_init_dos f_media_get_dos \
+ f_media_shutdown_dos "" "$capacity"
+ return $?
+ ;;
+ ufs/*|ufsid/*)
+ f_dprintf "Found UFS partition labeled %s" "$devname"
+ debug= f_device_register device "$devname" "UFS file system" \
+ "/dev/$devname" $DEVICE_TYPE_UFS 1 \
+ f_media_init_ufs f_media_get_ufs \
+ f_media_shutdown_ufs "" "$capacity"
+ return $?
+ ;;
+ ext2fs/*|ntfs/*|reiserfs/*)
+ return $FAILURE # No media device handlers for these labels
+ ;;
+ esac
+
+ # Unable to classify GEOM label
+ return $FAILURE
+}
+
+# f_device_probe_geom_part $var_to_set $gpart_type $devname $capacity [$magic]
+#
+# Given a gpart(8) partition type and a device name, register the device if it
+# is a known partition type that we can handle. If $var_to_set is non-NULL,
+# upon success holds the DEVICE struct name of the registered device.
+#
+# Returns success if the device was successfully registered, failure otherwise.
+#
+f_device_probe_geom_part()
+{
+ local __var_to_set="$1" __gpart_type="$2" __devname="$3"
+ local __capacity="${4:--1}" __magic="$5"
+
+ #
+ # Register device based on partition type
+ # NB: !0 equates to `unused' bsdlabel
+ #
+ case "$__gpart_type" in
+ fat16|fat32)
+ f_dprintf "Found DOS partition named %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "DOS file system" \
+ "/dev/$__devname" $DEVICE_TYPE_DOS 1 \
+ f_media_init_dos f_media_get_dos \
+ f_media_shutdown_dos "" "$__capacity"
+ return $?
+ ;;
+ freebsd|!0) # Commonly used inappropriately, taste for FreeBSD
+ [ -r "/dev/$__devname" -a ! "$__magic" ] &&
+ __magic=$( file -bs "/dev/$__devname" 2> /dev/null )
+ case "$__magic" in
+ *"Unix Fast File system"*)
+ f_dprintf "Found UFS partition named %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "UFS file system" \
+ "/dev/$__devname" $DEVICE_TYPE_UFS 1 \
+ f_media_init_ufs f_media_get_ufs \
+ f_media_shutdown_ufs "" "$__capacity"
+ return $?
+ esac
+ return $FAILURE
+ ;;
+ freebsd-ufs)
+ f_dprintf "Found UFS partition named %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "UFS file system" \
+ "/dev/$__devname" $DEVICE_TYPE_UFS 1 \
+ f_media_init_ufs f_media_get_ufs \
+ f_media_shutdown_ufs "" "$__capacity"
+ return $?
+ ;;
+ apple-*|linux-*|ms-*|netbsd-*|ntfs|vmware-*)
+ return $FAILURE # No device types for these
+ ;;
+ bios-*|ebr|efi|mbr|freebsd-boot|freebsd-swap)
+ return $FAILURE # Not a source for media
+ ;;
+ freebsd-nandfs|freebsd-vinum|freebsd-zfs)
+ return $FAILURE # Unsupported as media source
+ ;;
+ esac
+
+ return $FAILURE # Unknown partition type
+}
+
+# f_device_probe_disk_fs $var_to_set $devname [$capacity [$magic]]
+#
+# Given a device name, taste it and register the device if it is a so-called
+# ``dangerously dedicated'' file system written without a partition table.
+# Tasting is done using file(1) (specifically `file -bs') but if $magic is
+# present and non-NULL it is used instead. If $var_to_set is non-NULL, upon
+# success holds the DEVICE struct name of the registered device.
+#
+# Returns success if the device was successfully registered, failure otherwise.
+#
+f_device_probe_disk_fs()
+{
+ local __var_to_set="$1" __devname="$2" __capacity="${3:--1}"
+ local __magic="$4"
+
+ [ -r "/dev/${__devname#/dev/}" -a ! "$__magic" ] &&
+ __magic=$( file -bs "/dev/$__devname" 2> /dev/null )
+
+ case "$__magic" in
+ *"ISO 9660"*)
+ f_dprintf "Found CDROM device for disk %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "ISO9660 file system" \
+ "/dev/$__devname" $DEVICE_TYPE_CDROM 1 \
+ f_media_init_cdrom f_media_get_cdrom \
+ f_media_shutdown_cdrom "" "$__capacity"
+ return $?
+ ;;
+ *"Unix Fast File system"*)
+ f_dprintf "Found UFS device for disk %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "UFS file system" \
+ "/dev/$__devname" $DEVICE_TYPE_UFS 1 \
+ f_media_init_ufs f_media_get_ufs \
+ f_media_shutdown_ufs "" "$__capacity"
+ return $?
+ ;;
+ *"FAT (12 bit)"*|*"FAT (16 bit)"*|*"FAT (32 bit)"*)
+ f_dprintf "Found DOS device for disk %s" "$__devname"
+ debug= f_device_register "$__var_to_set" \
+ "$__devname" "DOS file system" \
+ "/dev/$__devname" $DEVICE_TYPE_DOS 1 \
+ f_media_init_dos f_media_get_dos \
+ f_media_shutdown_dos "" "$__capacity"
+ return $?
+ ;;
+ esac
+
+ return $FAILURE # Unknown file system type
+}
+
+# f_device_catalog_get $type $name [$var_to_set]
+#
+# Fetch the struct name of the catalog device matching device $name. If $type
+# is either NULL, missing, or set to $DEVICE_TYPE_ANY then only $name is used.
+# Returns success if a match was found, otherwise failure.
+#
+# If $var_to_set is missing or NULL, the struct name is printed to standard out
+# for capturing in a sub-shell (which is less-recommended because of
+# performance degredation; for example, when called in a loop).
+#
+f_device_catalog_get()
+{
+ local __type="$1" __name="$2" __var_to_set="$3"
+ local __dname=
+
+ # Return failure if no $name
+ [ "$__name" ] || return $FAILURE
+
+ # Disable debugging to keep debug output light
+ local debug=
+
+ #
+ # Attempt to create an alternate-form of $__name that contains the
+ # first contiguous string of numbers replaced with `%d' for comparison
+ # against stored pattern names (see MAIN).
+ #
+ local __left="${__name%%[0-9]*}" __right="${__name#*[0-9]}"
+ if [ "$__left" != "$__name" ]; then
+ # Chop leading digits from right 'til we hit first non-digit
+ while :; do
+ case "$__right" in
+ [0-9]*) __right="${__right#[0-9]}" ;;
+ *) break
+ esac
+ done
+ __dname="${__left}%d$__right"
+ fi
+
+ [ "$__type" = "$DEVICE_TYPE_ANY" ] && __type=
+ local __dev __dev_name __dev_type
+ for __dev in $DEVICE_CATALOG; do
+ catalog_device_$__dev get name __dev_name
+ [ "$__dev_name" = "$__name" -o "$__dev_name" = "$__dname" ] ||
+ continue
+ catalog_device_$__dev get type __dev_type
+ [ "${__type:-$__dev_type}" = "$__dev_type" ] || continue
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" $__dev
+ else
+ echo $__dev
+ fi
+ return $?
+ done
+
+ [ "$__var_to_set" ] && setvar "$__var_to_set" ""
+ return $FAILURE
+}
+
+# f_device_catalog_set $type $name $desc
+#
+# Store a description (desc) in-association with device $type and $name.
+# Returns success unless $name is NULL or missing. Use f_device_catalog_get()
+# routine with the same $name and optionally $type to retrieve catalog device
+# structure (see CATALOG_DEVICE struct definition in GLOBALS section).
+#
+f_device_catalog_set()
+{
+ local type="$1" name="$2" desc="$3"
+ local struct dev dev_type found=
+
+ [ "$name" ] || return $FAILURE
+
+ # Disable debugging to keep debug output light
+ local debug=
+
+ f_str2varname "$name" struct
+ if [ ! "$DEVICE_CATALOG_APPEND_ONLY" ]; then
+ for dev in $DEVICE_CATALOG; do
+ [ "$dev" = "$struct" ] || continue
+ found=1 break
+ done
+ fi
+ if [ "$found" ]; then
+ f_struct_free "catalog_device_$struct"
+ else
+ DEVICE_CATALOG="$DEVICE_CATALOG $struct"
+ fi
+ f_struct_new CATALOG_DEVICE "catalog_device_$struct" || return $FAILURE
+ catalog_device_$struct set type "$type"
+ catalog_device_$struct set name "$name"
+ catalog_device_$struct set desc "$desc"
+ return $SUCCESS
+}
+
+# f_device_desc $device_name $device_type [$var_to_set]
+#
+# Print a description for a device name (eg., `fxp0') given a specific device
+# type/class.
+#
+# If $var_to_set is missing or NULL, the device description is printed to
+# standard out for capturing in a sub-shell (which is less-recommended because
+# of performance degredation; for example, when called in a loop).
+#
+f_device_desc()
+{
+ local __name="$1" __type="$2" __var_to_set="$3"
+ local __devname __devunit __cp
+
+ # Check variables
+ [ "$__name" ] || return $SUCCESS
+ [ "$__type" = "$DEVICE_TYPE_ANY" ] && type=
+ [ "$__var_to_set" ] && { setvar "$__var_to_set" "" || return; }
+
+ #
+ # Return sysctl MIB dev.NAME.UNIT.%desc if it exists, otherwise fall
+ # through to further alternate methods.
+ #
+ if f_have sysctl; then
+ __devname="${__name%%[0-9]*}"
+ __devunit="${__name#$__devname}"
+ __devunit="${__devunit%%[!0-9]*}"
+ if [ "$__var_to_set" ]; then
+ if __cp=$(
+ sysctl -n "dev.$__devname.$__devunit.%desc" \
+ 2> /dev/null
+ ); then
+ setvar "$__var_to_set" "$__cp" &&
+ return $SUCCESS
+ fi
+ else
+ sysctl -n "dev.$__devname.$__devunit.%desc" \
+ 2> /dev/null && return $SUCCESS
+ fi
+ fi
+
+ # Look up device in catalog for default description
+ local __catalog_struct
+ debug= f_device_catalog_get "$__type" "$__name" __catalog_struct
+ debug= f_struct "catalog_device_$__catalog_struct" get \
+ desc "$__var_to_set" && return $SUCCESS
+
+ #
+ # Sensible fall-backs for specific types
+ #
+ case "$__type" in
+ $DEVICE_TYPE_CDROM) __cp="<unknown cdrom device type>" ;;
+ $DEVICE_TYPE_DISK) __cp="<unknown disk device type>" ;;
+ $DEVICE_TYPE_FLOPPY) __cp="<unknown floppy device type>" ;;
+ $DEVICE_TYPE_USB) __cp="<unknown USB storage device type>" ;;
+ $DEVICE_TYPE_NETWORK) __cp="<unknown network interface type>" ;;
+ *)
+ __cp="<unknown device type>"
+ esac
+
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__cp"
+ else
+ echo "$__cp"
+ fi
+
+ return $FAILURE
+}
+
+# f_device_is_ethernet $device
+#
+# Returns true if $device is a wired Ethernet network interface. Otherwise
+# returns false. Example wired interfaces include: fxp0 em0 bge0 rl0 etc.
+#
+f_device_is_ethernet()
+{
+ local dev="$1" type flags
+
+ # Make sure we have an actual device by that name
+ f_struct "$dev" || return $FAILURE
+
+ # Make sure that the device is a network device
+ $dev get type type
+ [ "$type" = "$DEVICE_TYPE_NETWORK" ] || return $FAILURE
+
+ # Make sure that the media flags indicate that it is Ethernet
+ $dev get flags flags
+ [ $(( ${flags:-0} & $IF_ETHERNET )) -eq $IF_ETHERNET ]
+}
+
+# f_device_is_wireless $device
+#
+# Returns true if $device is a Wireless network interface. Otherwise returns
+# false. Examples of wireless interfaces include: iwn0
+#
+f_device_is_wireless()
+{
+ local dev="$1" type flags
+
+ # Make sure we have an actual device by that name
+ f_struct "$dev" || return $FAILURE
+
+ # Make sure that the device is a network device
+ $dev get type type
+ [ "$type" = "$DEVICE_TYPE_NETWORK" ] || return $FAILURE
+
+ # Make sure that the media flags indicate that it is 802.11 wireless
+ $dev get flags flags
+ [ $(( ${flags:-0} & $IF_WIRELESS )) -eq $IF_WIRELESS ]
+}
+
+# f_device_is_active $device
+#
+# Returns true if $device is active. Otherwise returns false. Currently this
+# only works for network interfaces.
+#
+f_device_is_active()
+{
+ local dev="$1" type flags=0
+
+ # Make sure we have an actual device by that name
+ f_struct "$dev" || return $FAILURE
+
+ $dev get type type
+ case "$type" in
+ $DEVICE_TYPE_NETWORK)
+ # Make sure that the media flags indicate that it is active
+ $dev get flags flags
+ [ $(( ${flags:-0} & $IF_ACTIVE )) -eq $IF_ACTIVE ]
+ ;;
+ *)
+ return $FAILURE
+ esac
+}
+
+# f_device_find [-1] $name [$type [$var_to_set]]
+#
+# Find one or more registered devices by name, type, or both. Returns a space-
+# separated list of devices matching the search criterion.
+#
+# If `-1' option flag is given, only the first matching device is returned.
+#
+# If $var_to_set is missing or NULL, the device name(s) are printed to standard
+# out for capturing in a sub-shell (which is less-recommended because of
+# performance degredation; for example, when called in a loop).
+#
+f_device_find()
+{
+ local OPTIND OPTARG flag only_one=
+ while getopts 1 flag; do
+ case "$flag" in
+ 1) only_one=1 ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ local __name="$1" __type="${2:-$DEVICE_TYPE_ANY}" __var_to_set="$3"
+ local __n=1 __devname __devtype __found=
+ while [ $__n -le $NDEVICES ]; do
+ device_$__n get name __devname
+ device_$__n get type __devtype
+ if [ "$__name" = "$__devname" -o ! "$__name" ] &&
+ [ "$__type" = "$DEVICE_TYPE_ANY" -o \
+ "$__type" = "$__devtype" ]
+ then
+ __found="$__found device_$__n"
+ [ "$only_one" ] && break
+ fi
+ __n=$(( $__n + 1 ))
+ done
+
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "${__found# }"
+ else
+ echo $__found
+ fi
+ [ "$__found" ] # Return status
+}
+
+# f_device_init $device
+#
+# Initialize a device by evaluating its `init' function. The $device argument
+# is a DEVICE struct name.
+#
+f_device_init()
+{
+ local device="$1" init_func
+ f_struct "$device" || return $?
+ $device get init init_func
+ ${init_func:-:} "$device"
+}
+
+# f_device_get $device $file [$probe]
+#
+# Read $file by evaluating the device's `get' function. The file is commonly
+# produced on standard output (but it truly depends on the function called).
+# The $device argument is a DEVICE struct name.
+#
+f_device_get()
+{
+ local device="$1" file="$2" probe="$3" get_func
+ f_struct "$device" || return $?
+ $device get get get_func
+ ${get_func:-:} "$device" "$file" ${3+"$probe"}
+}
+
+# f_device_shutdown $device
+#
+# Shutdown a device by evaluating its `shutdown' function. The $device argument
+# is a DEVICE struct name.
+#
+f_device_shutdown()
+{
+ local device="$1" shutdown_func
+ f_struct "$device" || return $?
+ $device get shutdown shutdown_func
+ ${shutdown_func:-:} "$device"
+}
+
+# f_devices_sort_by $property $var_to_get [$var_to_set]
+#
+# Take list of devices from $var_to_get (separated by whitespace, newline
+# included) and sort them by $property (e.g., `name'). The sorted list of
+# DEVICE struct names is returned on standard output separated by whitespace
+# (newline to be specific) unless $var_to_set is present and non-NULL.
+#
+# 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_device_sort_by_awk='
+# Variables that should be defined on the invocation line:
+# -v prop="property"
+function _asorti(src, dest)
+{
+ k = nitems = 0
+ for (i in src) dest[++nitems] = i
+ for (i = 1; i <= nitems; k = i++) {
+ idx = dest[i]
+ while ((k > 0) && (dest[k] > idx)) {
+ dest[k+1] = dest[k]; k--
+ }
+ dest[k+1] = idx
+ }
+ return nitems
+}
+{
+ split($0, devs, FS)
+ for (d in devs) {
+ name = ENVIRON["_struct_value_" devs[d] "_" prop]
+ devices[name] = devs[d]
+ }
+}
+END {
+ nitems = _asorti(devices, devices_sorted)
+ for (i = 1; i <= nitems; i++) print devices[devices_sorted[i]]
+}
+'
+f_device_sort_by()
+{
+ local __property="${1:-name}" __var_to_get="$2" __var_to_set="$3"
+
+ f_isset "$__var_to_get" || return $FAILURE
+
+ local __dev
+ for __dev in $( f_getvar "$__var_to_get" ); do
+ export _struct_value_${__dev}_$__property
+ done
+
+ local __cp
+ setvar "${__var_to_set:-__cp}" "$(
+ f_getvar "$__var_to_get" |
+ awk -v prop="$__property" "$f_device_sort_by_awk"
+ )"
+ [ "$__var_to_set" ] || echo "$__cp"
+}
+
+# f_device_menu $title $prompt $hline $device_type [$helpfile]
+#
+# Display a menu listing all the devices of a certain type in the system.
+#
+f_device_menu()
+{
+ f_dialog_title "$1"
+ local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
+ f_dialog_title_restore
+
+ local prompt="$2" hline="$3" type="$4" helpfile="$5"
+
+ local devs
+ f_device_find "" "$type" devs || return $DIALOG_CANCEL
+
+ local name desc menu_list=
+ f_device_sort_by name devs devs
+ for dev in $devs; do
+ $dev get name name
+ $dev get desc desc
+ f_shell_escape "$name" name
+ f_shell_escape "$desc" desc
+ menu_list="$menu_list
+ '$name' '$desc'" # END-QUOTE
+ done
+ menu_list="${menu_list#$NL}"
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$title\" \
+ \"\$btitle\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local errexit=
+ case $- in *e*) errexit=1; esac
+ set +e
+
+ local mtag
+ while :; do
+ mtag=$( eval $DIALOG \
+ --title \"\$title\" \
+ --backtitle \"\$btitle\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_cancel\" \
+ ${helpfile:+ \
+ --help-button \
+ --help-label \"\$msg_help\" \
+ ${USE_XDIALOG:+--help \"\"} \
+ } \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+
+ [ $retval -ne $DIALOG_HELP ] && break
+ # Otherwise, the Help button was pressed
+ f_show_help "$helpfile"
+ # ...then loop back to menu
+ done
+ f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
+
+ [ "$errexit" ] && set -e
+
+ if [ $retval -eq $DIALOG_OK ]; then
+ # Clean up the output of [X]dialog(1)
+ f_dialog_data_sanitize mtag
+
+ # Map the user's choice back to a struct name
+ local index device
+ index=$( eval f_dialog_menutag2index \"\$mtag\" $menu_list )
+ device=$( set -- $devs; eval echo \${$index} )
+
+ echo "$device" >&2
+ fi
+
+ return $retval
+}
+
+#
+# Short-hand
+#
+f_cdrom() { f_device_catalog_set $DEVICE_TYPE_CDROM "$1" "$2"; }
+f_disk() { f_device_catalog_set $DEVICE_TYPE_DISK "$1" "$2"; }
+f_floppy() { f_device_catalog_set $DEVICE_TYPE_FLOPPY "$1" "$2"; }
+f_usb() { f_device_catalog_set $DEVICE_TYPE_USB "$1" "$2"; }
+f_network() { f_device_catalog_set $DEVICE_TYPE_NETWORK "$1" "$2"; }
+
+############################################################ MAIN
+
+#
+# The below classifications allow us to re-group the GEOM devices from the
+# `DEV' GEOM class appropriately while providing fall-back descriptions both
+# for making the below code more maintainable and handling the rare case the
+# GEOM device lacks a description.
+#
+
+DEVICE_CATALOG_APPEND_ONLY=1 # Make initial loading faster
+
+# CDROM, Disk, Floppy, and USB devices/names
+f_cdrom "cd%d" "SCSI CDROM drive"
+f_cdrom "mcd%d" "Mitsumi (old model) CDROM drive"
+f_cdrom "scd%d" "Sony CDROM drive - CDU31/33A type"
+f_disk "aacd%d" "Adaptec FSA RAID array"
+f_disk "ada%d" "ATA/SATA disk device"
+f_disk "amrd%d" "AMI MegaRAID drive"
+f_disk "da%d" "SCSI disk device"
+f_disk "idad%d" "Compaq RAID array"
+f_disk "ipsd%d" "IBM ServeRAID RAID array"
+f_disk "md%d" "md(4) disk device"
+f_disk "mfid%d" "LSI MegaRAID SAS array"
+f_disk "mlxd%d" "Mylex RAID disk"
+f_disk "twed%d" "3ware ATA RAID array"
+f_disk "vtbd%d" "VirtIO Block Device"
+f_floppy "fd%d" "Floppy Drive unit A"
+f_usb "da%da" "USB Mass Storage Device"
+
+# Network interfaces/names
+f_network "ae%d" "Attansic/Atheros L2 Fast Ethernet"
+f_network "age%d" "Attansic/Atheros L1 Gigabit Ethernet"
+f_network "alc%d" "Atheros AR8131/AR8132 PCIe Ethernet"
+f_network "ale%d" "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"
+f_network "an%d" "Aironet 4500/4800 802.11 wireless adapter"
+f_network "ath%d" "Atheros IEEE 802.11 wireless adapter"
+f_network "aue%d" "ADMtek USB Ethernet adapter"
+f_network "axe%d" "ASIX Electronics USB Ethernet adapter"
+f_network "bce%d" "Broadcom NetXtreme II Gigabit Ethernet card"
+f_network "bfe%d" "Broadcom BCM440x PCI Ethernet card"
+f_network "bge%d" "Broadcom BCM570x PCI Gigabit Ethernet card"
+f_network "bm%d" "Apple BMAC Built-in Ethernet"
+f_network "bwn%d" "Broadcom BCM43xx IEEE 802.11 wireless adapter"
+f_network "cas%d" "Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet"
+f_network "cc3i%d" "SDL HSSI sync serial PCI card"
+f_network "cue%d" "CATC USB Ethernet adapter"
+f_network "cxgb%d" "Chelsio T3 10Gb Ethernet card"
+f_network "dc%d" "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"
+f_network "de%d" "DEC DE435 PCI NIC or other DC21040-AA based card"
+f_network "disc%d" "Software discard network interface"
+f_network "ed%d" "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"
+f_network "el%d" "3Com 3C501 Ethernet card"
+f_network "em%d" "Intel(R) PRO/1000 Ethernet card"
+f_network "en%d" "Efficient Networks ATM PCI card"
+f_network "ep%d" "3Com 3C509 Ethernet card/3C589 PCMCIA"
+f_network "et%d" "Agere ET1310 based PCI Express Gigabit Ethernet card"
+f_network "ex%d" "Intel EtherExpress Pro/10 Ethernet card"
+f_network "fe%d" "Fujitsu MB86960A/MB86965A Ethernet card"
+f_network "fpa%d" "DEC DEFPA PCI FDDI card"
+f_network "fwe%d" "FireWire Ethernet emulation"
+f_network "fwip%d" "IP over FireWire"
+f_network "fxp%d" "Intel EtherExpress Pro/100B PCI Fast Ethernet card"
+f_network "gem%d" "Apple GMAC or Sun ERI/GEM Ethernet adapter"
+f_network "hme%d" "Sun HME (Happy Meal Ethernet) Ethernet adapter"
+f_network "ie%d" "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"
+f_network "igb%d" "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"
+f_network "ipw%d" "Intel PRO/Wireless 2100 IEEE 802.11 adapter"
+f_network "iwi%d" "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"
+f_network "iwn%d" "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"
+f_network "ix%d" "Intel Etherexpress Ethernet card"
+f_network "ixgb%d" "Intel(R) PRO/10Gb Ethernet card"
+f_network "ixgbe%d" "Intel(R) PRO/10Gb Ethernet card"
+f_network "jme%d" "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"
+f_network "kue%d" "Kawasaki LSI USB Ethernet adapter"
+f_network "le%d" "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"
+f_network "lge%d" "Level 1 LXT1001 Gigabit Ethernet card"
+f_network "lnc%d" "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet"
+f_network "lo%d" "Loop-back (local) network interface"
+f_network "lp%d" "Parallel Port IP (PLIP) peer connection"
+f_network "malo%d" "Marvell Libertas 88W8335 802.11 wireless adapter"
+f_network "msk%d" "Marvell/SysKonnect Yukon II Gigabit Ethernet"
+f_network "mxge%d" "Myricom Myri10GE 10Gb Ethernet card"
+f_network "nfe%d" "NVIDIA nForce MCP Ethernet"
+f_network "ng%d" "Vimage netgraph(4) bridged Ethernet device"
+f_network "nge%d" "NatSemi PCI Gigabit Ethernet card"
+f_network "nve%d" "NVIDIA nForce MCP Ethernet"
+f_network "nxge%d" "Neterion Xframe 10GbE Server/Storage adapter"
+f_network "pcn%d" "AMD Am79c79x PCI Ethernet card"
+f_network "plip%d" "Parallel Port IP (PLIP) peer connection"
+f_network "ral%d" "Ralink Technology IEEE 802.11 wireless adapter"
+f_network "ray%d" "Raytheon Raylink 802.11 wireless adapter"
+f_network "re%d" "RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter"
+f_network "rl%d" "RealTek 8129/8139 PCI Ethernet card"
+f_network "rue%d" "RealTek USB Ethernet card"
+f_network "rum%d" "Ralink Technology USB IEEE 802.11 wireless adapter"
+f_network "sf%d" "Adaptec AIC-6915 PCI Ethernet card"
+f_network "sge%d" "Silicon Integrated Systems SiS190/191 Ethernet"
+f_network "sis%d" "SiS 900/SiS 7016 PCI Ethernet card"
+f_network "sk%d" "SysKonnect PCI Gigabit Ethernet card"
+f_network "sn%d" "SMC/Megahertz Ethernet card"
+f_network "snc%d" "SONIC Ethernet card"
+f_network "sr%d" "SDL T1/E1 sync serial PCI card"
+f_network "ste%d" "Sundance ST201 PCI Ethernet card"
+f_network "stge%d" "Sundance/Tamarack TC9021 Gigabit Ethernet"
+f_network "ti%d" "Alteon Networks PCI Gigabit Ethernet card"
+f_network "tl%d" "Texas Instruments ThunderLAN PCI Ethernet card"
+f_network "tx%d" "SMC 9432TX Ethernet card"
+f_network "txp%d" "3Com 3cR990 Ethernet card"
+f_network "uath%d" "Atheros AR5005UG and AR5005UX USB wireless adapter"
+f_network "upgt%d" "Conexant/Intersil PrismGT USB wireless adapter"
+f_network "ural%d" "Ralink Technology RT2500USB 802.11 wireless adapter"
+f_network "urtw%d" "Realtek 8187L USB wireless adapter"
+f_network "vge%d" "VIA VT612x PCI Gigabit Ethernet card"
+f_network "vlan%d" "IEEE 802.1Q VLAN network interface"
+f_network "vr%d" "VIA VT3043/VT86C100A Rhine PCI Ethernet card"
+f_network "vx%d" "3COM 3c590 / 3c595 Ethernet card"
+f_network "wb%d" "Winbond W89C840F PCI Ethernet card"
+f_network "wi%d" "Lucent WaveLAN/IEEE 802.11 wireless adapter"
+f_network "wpi%d" "Intel 3945ABG IEEE 802.11 wireless adapter"
+f_network "wx%d" "Intel Gigabit Ethernet (82452) card"
+f_network "xe%d" "Xircom/Intel EtherExpress Pro100/16 Ethernet card"
+f_network "xl%d" "3COM 3c90x / 3c90xB PCI Ethernet card"
+f_network "zyd%d" "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"
+
+DEVICE_CATALOG_APPEND_ONLY= # Additional loading modifies existing devices
+
+f_count NCATALOG_DEVICES $DEVICE_CATALOG
+f_dprintf "%s: Initialized device catalog with %u names/descriptions." \
+ device.subr $NCATALOG_DEVICES
+
+#
+# Scan for the above devices unless requeted otherwise
+#
+f_dprintf "%s: DEVICE_SELF_SCAN_ALL=[%s]" device.subr "$DEVICE_SELF_SCAN_ALL"
+case "$DEVICE_SELF_SCAN_ALL" in
+""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
+*) f_device_get_all
+esac
+
+f_dprintf "%s: Successfully loaded." device.subr
+
+fi # ! $_DEVICE_SUBR
diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr
new file mode 100644
index 0000000..d7c2d2c
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/dialog.subr
@@ -0,0 +1,2341 @@
+if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1
+#
+# Copyright (c) 2006-2015 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." dialog.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ CONFIGURATION
+
+#
+# Default file descriptor to link to stdout for dialog(1) passthru allowing
+# execution of dialog from within a sub-shell (so-long as its standard output
+# is explicitly redirected to this file descriptor).
+#
+: ${DIALOG_TERMINAL_PASSTHRU_FD:=${TERMINAL_STDOUT_PASSTHRU:-3}}
+
+############################################################ 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
+
+#
+# Exit codes for [X]dialog(1)
+#
+DIALOG_OK=${SUCCESS:-0}
+DIALOG_CANCEL=${FAILURE:-1}
+DIALOG_HELP=2
+DIALOG_ITEM_HELP=2
+DIALOG_EXTRA=3
+DIALOG_ITEM_HELP=4
+export DIALOG_ERROR=254 # sh(1) can't handle the default of `-1'
+DIALOG_ESC=255
+
+#
+# Default behavior is to call f_dialog_init() automatically when loaded.
+#
+: ${DIALOG_SELF_INITIALIZE=1}
+
+#
+# Default terminal size (used if/when running without a controlling terminal)
+#
+: ${DEFAULT_TERMINAL_SIZE:=24 80}
+
+#
+# Minimum width(s) for various dialog(1) implementations (sensible global
+# default(s) for all widgets of a given variant)
+#
+: ${DIALOG_MIN_WIDTH:=24}
+: ${XDIALOG_MIN_WIDTH:=35}
+
+#
+# When manually sizing Xdialog(1) widgets such as calendar and timebox, you'll
+# need to know the size of the embedded GUI objects because the height passed
+# to Xdialog(1) for these widgets has to be tall enough to accomodate them.
+#
+# These values are helpful when manually sizing with dialog(1) too, but in a
+# different way. dialog(1) does not make you accomodate the custom items in the
+# height (but does for width) -- a height of 3 will display three lines and a
+# full calendar, for example (whereas Xdialog will truncate the calendar if
+# given a height of 3). For dialog(1), use these values for making sure that
+# the height does not exceed max_height (obtained by f_dialog_max_size()).
+#
+DIALOG_CALENDAR_HEIGHT=15
+DIALOG_TIMEBOX_HEIGHT=6
+
+############################################################ GENERIC FUNCTIONS
+
+# f_dialog_data_sanitize $var_to_edit ...
+#
+# When using dialog(1) or Xdialog(1) sometimes unintended warnings or errors
+# are generated from underlying libraries. For example, if $LANG is set to an
+# invalid or unknown locale, the warnings from the Xdialog(1) libraries will
+# clutter the output. This function helps by providing a centralied function
+# that removes spurious warnings from the dialog(1) (or Xdialog(1)) response.
+#
+# Simply pass the name of one or more variables that need to be sanitized.
+# After execution, the variables will hold their newly-sanitized data.
+#
+f_dialog_data_sanitize()
+{
+ if [ "$#" -eq 0 ]; then
+ f_dprintf "%s: called with zero arguments" \
+ f_dialog_response_sanitize
+ return $FAILURE
+ fi
+
+ local __var_to_edit
+ for __var_to_edit in $*; do
+ # Skip warnings and trim leading/trailing whitespace
+ setvar $__var_to_edit "$( f_getvar $__var_to_edit | awk '
+ BEGIN { data = 0 }
+ {
+ if ( ! data )
+ {
+ if ( $0 ~ /^$/ ) next
+ if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
+ data = 1
+ }
+ print
+ }
+ ' )"
+ done
+}
+
+# f_dialog_line_sanitize $var_to_edit ...
+#
+# When using dialog(1) or Xdialog(1) sometimes unintended warnings or errors
+# are generated from underlying libraries. For example, if $LANG is set to an
+# invalid or unknown locale, the warnings from the Xdialog(1) libraries will
+# clutter the output. This function helps by providing a centralied function
+# that removes spurious warnings from the dialog(1) (or Xdialog(1)) response.
+#
+# Simply pass the name of one or more variables that need to be sanitized.
+# After execution, the variables will hold their newly-sanitized data.
+#
+# This function, unlike f_dialog_data_sanitize(), also removes leading/trailing
+# whitespace from each line.
+#
+f_dialog_line_sanitize()
+{
+ if [ "$#" -eq 0 ]; then
+ f_dprintf "%s: called with zero arguments" \
+ f_dialog_response_sanitize
+ return $FAILURE
+ fi
+
+ local __var_to_edit
+ for __var_to_edit in $*; do
+ # Skip warnings and trim leading/trailing whitespace
+ setvar $__var_to_edit "$( f_getvar $__var_to_edit | awk '
+ BEGIN { data = 0 }
+ {
+ if ( ! data )
+ {
+ if ( $0 ~ /^$/ ) next
+ if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
+ data = 1
+ }
+ sub(/^[[:space:]]*/, "")
+ sub(/[[:space:]]*$/, "")
+ print
+ }
+ ' )"
+ done
+}
+
+############################################################ TITLE 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 [ "${1+set}" ]; 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 [ "${1+set}" ]; 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_max_size $var_height $var_width
+#
+# Get the maximum height and width for a dialog widget and store the values in
+# $var_height and $var_width (respectively).
+#
+f_dialog_max_size()
+{
+ local funcname=f_dialog_max_size
+ local __var_height="$1" __var_width="$2" __max_size
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+ if [ "$USE_XDIALOG" ]; then
+ __max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
+ else
+ if __max_size=$( $DIALOG --print-maxsize \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD )
+ then
+ f_dprintf "$funcname: %s --print-maxsize = [%s]" \
+ "$DIALOG" "$__max_size"
+ # usually "MaxSize: 24, 80"
+ __max_size="${__max_size#*: }"
+ f_replaceall "$__max_size" "," "" __max_size
+ else
+ f_eval_catch -dk __max_size $funcname stty \
+ 'stty size' || __max_size=
+ # usually "24 80"
+ fi
+ : ${__max_size:=$DEFAULT_TERMINAL_SIZE}
+ fi
+ if [ "$__var_height" ]; then
+ local __height="${__max_size%%[$IFS]*}"
+ #
+ # If we're not using Xdialog(1), we should assume that $DIALOG
+ # will render --backtitle behind the widget. In such a case, we
+ # should prevent a widget from obscuring the backtitle (unless
+ # $NO_BACKTITLE is set and non-NULL, allowing a trap-door).
+ #
+ if [ ! "$USE_XDIALOG" ] && [ ! "$NO_BACKTITLE" ]; then
+ #
+ # If use_shadow (in ~/.dialogrc) is OFF, we need to
+ # subtract 4, otherwise 5. However, don't check this
+ # every time, rely on an initialization variable set
+ # by f_dialog_init().
+ #
+ local __adjust=5
+ [ "$NO_SHADOW" ] && __adjust=4
+
+ # Don't adjust height if already too small (allowing
+ # obscured backtitle for small values of __height).
+ [ ${__height:-0} -gt 11 ] &&
+ __height=$(( $__height - $__adjust ))
+ fi
+ setvar "$__var_height" "$__height"
+ fi
+ [ "$__var_width" ] && setvar "$__var_width" "${__max_size##*[$IFS]}"
+}
+
+# f_dialog_size_constrain $var_height $var_width [$min_height [$min_width]]
+#
+# Modify $var_height to be no-less-than $min_height (if given; zero otherwise)
+# and no-greater-than terminal height (or screen height if $USE_XDIALOG is
+# set).
+#
+# Also modify $var_width to be no-less-than $XDIALOG_MIN_WIDTH (or
+# $XDIALOG_MIN_WIDTH if $_USE_XDIALOG is set) and no-greater-than terminal
+# or screen width. The use of $[X]DIALOG_MIN_WIDTH can be overridden by
+# passing $min_width.
+#
+# Return status is success unless one of the passed arguments is invalid
+# or all of the $var_* arguments are either NULL or missing.
+#
+f_dialog_size_constrain()
+{
+ local __var_height="$1" __var_width="$2"
+ local __min_height="$3" __min_width="$4"
+ local __retval=$SUCCESS
+
+ # Return failure unless at least one var_* argument is passed
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ #
+ # Print debug warnings if any given (non-NULL) argument are invalid
+ # NOTE: Don't change the name of $__{var,min,}{height,width}
+ #
+ local __height __width
+ local __arg __cp __fname=f_dialog_size_constrain
+ for __arg in height width; do
+ debug= f_getvar __var_$__arg __cp
+ [ "$__cp" ] || continue
+ if ! debug= f_getvar "$__cp" __$__arg; then
+ f_dprintf "%s: var_%s variable \`%s' not set" \
+ $__fname $__arg "$__cp"
+ __retval=$FAILURE
+ elif ! eval f_isinteger \$__$__arg; then
+ f_dprintf "%s: var_%s variable value not a number" \
+ $__fname $__arg
+ __retval=$FAILURE
+ fi
+ done
+ for __arg in height width; do
+ debug= f_getvar __min_$__arg __cp
+ [ "$__cp" ] || continue
+ f_isinteger "$__cp" && continue
+ f_dprintf "%s: min_%s value not a number" $__fname $__arg
+ __retval=$FAILURE
+ setvar __min_$__arg ""
+ done
+
+ # Obtain maximum height and width values
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __max_height_size_constain __max_width_size_constrain
+ f_dialog_max_size \
+ __max_height_size_constrain __max_width_size_constrain
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ if [ $__height -lt ${__min_height:-0} ]; then
+ setvar "$__var_height" $__min_height
+ elif [ $__height -gt $__max_height_size_constrain ]; then
+ setvar "$__var_height" $__max_height_size_constrain
+ fi
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ if [ "$USE_XDIALOG" ]; then
+ : ${__min_width:=${XDIALOG_MIN_WIDTH:-35}}
+ else
+ : ${__min_width:=${DIALOG_MIN_WIDTH:-24}}
+ fi
+ if [ $__width -lt $__min_width ]; then
+ setvar "$__var_width" $__min_width
+ elif [ $__width -gt $__max_width_size_constrain ]; then
+ setvar "$__var_width" $__max_width_size_constrain
+ fi
+ fi
+
+ if [ "$debug" ]; then
+ # Print final constrained values to debugging
+ [ "$__var_height" ] && f_quietly f_getvar "$__var_height"
+ [ "$__var_width" ] && f_quietly f_getvar "$__var_width"
+ fi
+
+ return $__retval # success if no debug warnings were printed
+}
+
+# f_dialog_menu_constrain $var_height $var_width $var_rows "$prompt" \
+# [$min_height [$min_width [$min_rows]]]
+#
+# Modify $var_height to be no-less-than $min_height (if given; zero otherwise)
+# and no-greater-than terminal height (or screen height if $USE_XDIALOG is
+# set).
+#
+# Also modify $var_width to be no-less-than $XDIALOG_MIN_WIDTH (or
+# $XDIALOG_MIN_WIDTH if $_USE_XDIALOG is set) and no-greater-than terminal
+# or screen width. The use of $[X]DIALOG_MIN_WIDTH can be overridden by
+# passing $min_width.
+#
+# Last, modify $var_rows to be no-less-than $min_rows (if specified; zero
+# otherwise) and no-greater-than (max_height - 8) where max_height is the
+# terminal height (or screen height if $USE_XDIALOG is set). If $prompt is NULL
+# or missing, dialog(1) allows $var_rows to be (max_height - 7), maximizing the
+# number of visible rows.
+#
+# Return status is success unless one of the passed arguments is invalid
+# or all of the $var_* arguments are either NULL or missing.
+#
+f_dialog_menu_constrain()
+{
+ local __var_height="$1" __var_width="$2" __var_rows="$3" __prompt="$4"
+ local __min_height="$5" __min_width="$6" __min_rows="$7"
+
+ # Return failure unless at least one var_* argument is passed
+ [ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
+ return $FAILURE
+
+ #
+ # Print debug warnings if any given (non-NULL) argument are invalid
+ # NOTE: Don't change the name of $__{var,min,}{height,width,rows}
+ #
+ local __height_menu_constrain __width_menu_constrain
+ local __rows_menu_constrain
+ local __arg __cp __fname=f_dialog_menu_constrain
+ for __arg in height width rows; do
+ debug= f_getvar __var_$__arg __cp
+ [ "$__cp" ] || continue
+ if ! debug= f_getvar "$__cp" __${__arg}_menu_constrain; then
+ f_dprintf "%s: var_%s variable \`%s' not set" \
+ $__fname $__arg "$__cp"
+ __retval=$FAILURE
+ elif ! eval f_isinteger \$__${__arg}_menu_constrain; then
+ f_dprintf "%s: var_%s variable value not a number" \
+ $__fname $__arg
+ __retval=$FAILURE
+ fi
+ done
+ for __arg in height width rows; do
+ debug= f_getvar __min_$__arg __cp
+ [ "$__cp" ] || continue
+ f_isinteger "$__cp" && continue
+ f_dprintf "%s: min_%s value not a number" $__fname $__arg
+ __retval=$FAILURE
+ setvar __min_$__arg ""
+ done
+
+ # Obtain maximum height and width values
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __max_height_menu_constrain __max_width_menu_constrain
+ f_dialog_max_size \
+ __max_height_menu_constrain __max_width_menu_constrain
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ if [ $__height_menu_constrain -lt ${__min_height:-0} ]; then
+ setvar "$__var_height" $__min_height
+ elif [ $__height_menu_constrain -gt \
+ $__max_height_menu_constrain ]
+ then
+ setvar "$__var_height" $__max_height_menu_constrain
+ fi
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ if [ "$USE_XDIALOG" ]; then
+ : ${__min_width:=${XDIALOG_MIN_WIDTH:-35}}
+ else
+ : ${__min_width:=${DIALOG_MIN_WIDTH:-24}}
+ fi
+ if [ $__width_menu_constrain -lt $__min_width ]; then
+ setvar "$__var_width" $__min_width
+ elif [ $__width_menu_constrain -gt \
+ $__max_width_menu_constrain ]
+ then
+ setvar "$__var_width" $__max_width_menu_constrain
+ fi
+ fi
+
+ # Adjust rows if desired
+ if [ "$__var_rows" ]; then
+ if [ "$USE_XDIALOG" ]; then
+ : ${__min_rows:=1}
+ else
+ : ${__min_rows:=0}
+ fi
+
+ local __max_rows_menu_constrain=$((
+ $__max_height_menu_constrain - 7
+ ))
+ # If prompt_len is zero (no prompt), bump the max-rows by 1
+ # Default assumption is (if no argument) that there's no prompt
+ [ ${__prompt_len:-0} -gt 0 ] || __max_rows_menu_constrain=$((
+ $__max_rows_menu_constrain + 1
+ ))
+
+ if [ $__rows_menu_constrain -lt $__min_rows ]; then
+ setvar "$__var_rows" $__min_rows
+ elif [ $__rows_menu_constrain -gt $__max_rows_menu_constrain ]
+ then
+ setvar "$__var_rows" $__max_rows_menu_constrain
+ fi
+ fi
+
+ if [ "$debug" ]; then
+ # Print final constrained values to debugging
+ [ "$__var_height" ] && f_quietly f_getvar "$__var_height"
+ [ "$__var_width" ] && f_quietly f_getvar "$__var_width"
+ [ "$__var_rows" ] && f_quietly f_getvar "$__var_rows"
+ fi
+
+ return $__retval # success if no debug warnings were printed
+}
+
+# f_dialog_infobox_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are the
+# title, backtitle, prompt, and [optionally] hline. The optimal height and
+# width for the described widget (not exceeding the actual terminal height or
+# width) is stored in $var_height and $var_width (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+f_dialog_infobox_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ # Default height/width of zero for auto-sizing
+ local __height=0 __width=0 __n
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ #
+ # Set height based on number of rows in prompt
+ #
+ __n=$( echo -n "$__prompt" | f_number_of_lines )
+ __n=$(( $__n + 2 ))
+ [ $__n -gt $__height ] && __height=$__n
+
+ #
+ # 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
+
+ setvar "$__var_height" $__height
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ #
+ # Bump width for long titles
+ #
+ __n=$(( ${#__title} + 4 ))
+ [ $__n -gt $__width ] && __width=$__n
+
+ #
+ # If using Xdialog(1), bump width for long backtitles (which
+ # appear within the window).
+ #
+ if [ "$USE_XDIALOG" ]; then
+ __n=$(( ${#__btitle} + 4 ))
+ [ $__n -gt $__width ] && __width=$__n
+ fi
+
+ #
+ # Bump width for long prompts
+ #
+ __n=$( echo "$__prompt" | f_longest_line_length )
+ __n=$(( $__n + 4 )) # add width for border
+ [ $__n -gt $__width ] && __width=$__n
+
+ #
+ # Bump width for long hlines. Xdialog(1) supports `--hline' but
+ # it's currently not used (so don't do anything here if using
+ # Xdialog(1)).
+ #
+ if [ ! "$USE_XDIALOG" ]; then
+ __n=$(( ${#__hline} + 10 ))
+ [ $__n -gt $__width ] && __width=$__n
+ fi
+
+ # Bump width by 16.6% if using Xdialog(1)
+ [ "$USE_XDIALOG" ] && __width=$(( $__width + $__width / 6 ))
+
+ setvar "$__var_width" $__width
+ fi
+
+ # Constrain values to sensible minimums/maximums unless `-n' was passed
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] ||
+ f_dialog_size_constrain "$__var_height" "$__var_width"
+}
+
+# f_dialog_buttonbox_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are the
+# title, backtitle, prompt, and [optionally] hline. The optimal height and
+# width for the described widget (not exceeding the actual terminal height or
+# width) is stored in $var_height and $var_width (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+f_dialog_buttonbox_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ # Calculate height/width of infobox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_bbox_size __width_bbox_size
+ f_dialog_infobox_size -n \
+ "${__var_height:+__height_bbox_size}" \
+ "${__var_width:+__width_bbox_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add height to accomodate the buttons
+ __height_bbox_size=$(( $__height_bbox_size + 2 ))
+
+ # Adjust for clipping with Xdialog(1) on Linux/GTK2
+ [ "$USE_XDIALOG" ] &&
+ __height_bbox_size=$(( $__height_bbox_size + 3 ))
+
+ setvar "$__var_height" $__height_bbox_size
+ fi
+
+ # No adjustemnts to width, just pass-thru the infobox width
+ if [ "$__var_width" ]; then
+ setvar "$__var_width" $__width_bbox_size
+ fi
+
+ # Constrain values to sensible minimums/maximums unless `-n' was passed
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] ||
+ f_dialog_size_constrain "$__var_height" "$__var_width"
+}
+
+# f_dialog_inputbox_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are the
+# title, backtitle, prompt, and [optionally] hline. The optimal height and
+# width for the described widget (not exceeding the actual terminal height or
+# width) is stored in $var_height and $var_width (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+f_dialog_inputbox_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5" __init="$6" __hline="$7"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ # Calculate height/width of buttonbox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_ibox_size __width_ibox_size
+ f_dialog_buttonbox_size -n \
+ "${__var_height:+__height_ibox_size}" \
+ "${__var_width:+__width_ibox_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add height for input box (not needed for Xdialog(1))
+ [ ! "$USE_XDIALOG" ] &&
+ __height_ibox_size=$(( $__height_ibox_size + 3 ))
+
+ setvar "$__var_height" $__height_ibox_size
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ # Bump width for initial text (something neither dialog(1) nor
+ # Xdialog(1) do, but worth it!; add 16.6% if using Xdialog(1))
+ local __n=$(( ${#__init} + 7 ))
+ [ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 ))
+ [ $__n -gt $__width_ibox_size ] && __width_ibox_size=$__n
+
+ setvar "$__var_width" $__width_ibox_size
+ fi
+
+ # Constrain values to sensible minimums/maximums unless `-n' was passed
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] ||
+ f_dialog_size_constrain "$__var_height" "$__var_width"
+}
+
+# f_xdialog_2inputsbox_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are 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. The
+# optimal height and width for the described widget (not exceeding the actual
+# terminal height or width) is stored in $var_height and $var_width
+# (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# Xdialog(1).
+#
+f_xdialog_2inputsbox_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5"
+ local __label1="$6" __init1="$7" __label2="$8" __init2="$9"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ # Calculate height/width of inputbox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_2ibox_size __width_2ibox_size
+ f_dialog_inputbox_size -n \
+ "${__var_height:+__height_2ibox_size}" \
+ "${__var_width:+__width_2ibox_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline" "$__init1"
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add height for 1st label, 2nd label, and 2nd input box
+ __height_2ibox_size=$(( $__height_2ibox_size + 2 + 2 + 2 ))
+ setvar "$__var_height" $__height_2ibox_size
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ local __n
+
+ # Bump width for first label text (+16.6% since Xdialog(1))
+ __n=$(( ${#__label1} + 7 ))
+ __n=$(( $__n + $__n / 6 ))
+ [ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
+
+ # Bump width for second label text (+16.6% since Xdialog(1))
+ __n=$(( ${#__label2} + 7 ))
+ __n=$(( $__n + $__n / 6 ))
+ [ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
+
+ # Bump width for 2nd initial text (something neither dialog(1)
+ # nor Xdialog(1) do, but worth it!; +16.6% since Xdialog(1))
+ __n=$(( ${#__init2} + 7 ))
+ __n=$(( $__n + $__n / 6 ))
+ [ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
+
+ setvar "$__var_width" $__width_2ibox_size
+ fi
+
+ # Constrain values to sensible minimums/maximums unless `-n' was passed
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] ||
+ f_dialog_size_constrain "$__var_height" "$__var_width"
+}
+
+# f_dialog_menu_size [-n] $var_height $var_width $var_rows \
+# $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 three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the menu list itself (comprised of tag/item couplets). The
+# optimal height, width, and rows for the described widget (not exceeding the
+# actual terminal height or width) is stored in $var_height, $var_width, and
+# $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_menu_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2" __var_rows="$3"
+ local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
+ shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
+ return $FAILURE
+
+ # Calculate height/width of infobox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_menu_size __width_menu_size
+ f_dialog_infobox_size -n \
+ "${__var_height:+__height_menu_size}" \
+ "${__var_width:+__width_menu_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ #
+ # Always process the menu-item arguments to get the longest tag-length,
+ # longest item-length (both used to bump the width), and the number of
+ # rows (used to bump the height).
+ #
+ local __longest_tag=0 __longest_item=0 __rows=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=$(( $__rows + 1 ))
+ done
+
+ # Adjust rows early (for up-comning height calculation)
+ if [ "$__var_height" -o "$__var_rows" ]; then
+ # Add a row for visual aid if using Xdialog(1)
+ [ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
+ fi
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add rows to height
+ if [ "$USE_XDIALOG" ]; then
+ __height_menu_size=$((
+ $__height_menu_size + $__rows + 7 ))
+ else
+ __height_menu_size=$((
+ $__height_menu_size + $__rows + 4 ))
+ fi
+ setvar "$__var_height" $__height_menu_size
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ # The sum total between the longest tag-length and the
+ # longest item-length should be used to bump menu width
+ local __n=$(( $__longest_tag + $__longest_item + 10 ))
+ [ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_menu_size ] && __width_menu_size=$__n
+
+ setvar "$__var_width" $__width_menu_size
+ fi
+
+ # Store adjusted rows if desired
+ [ "$__var_rows" ] && setvar "$__var_rows" $__rows
+
+ # Constrain height, width, and rows to sensible minimum/maximum values
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] || f_dialog_menu_constrain \
+ "$__var_height" "$__var_width" "$__var_rows" "$__prompt"
+}
+
+# f_dialog_menu_with_help_size [-n] $var_height $var_width $var_rows \
+# $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 three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the menu list itself (comprised of tag/item/help triplets). The
+# optimal height, width, and rows for the described widget (not exceeding the
+# actual terminal height or width) is stored in $var_height, $var_width, and
+# $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_menu_with_help_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2" __var_rows="$3"
+ local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
+ shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
+ return $FAILURE
+
+ # Calculate height/width of infobox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_menu_with_help_size __width_menu_with_help_size
+ f_dialog_infobox_size -n \
+ "${__var_height:+__height_menu_with_help_size}" \
+ "${__var_width:+__width_menu_with_help_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ #
+ # Always process the menu-item arguments to get the longest tag-length,
+ # longest item-length, longest help-length (help-length only considered
+ # if using Xdialog(1), as it places the help string in the widget) --
+ # all used to bump the width -- and the number of rows (used to bump
+ # the height).
+ #
+ local __longest_tag=0 __longest_item=0 __longest_help=0 __rows=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=$(( $__rows + 1 ))
+ done
+
+ # Adjust rows early (for up-coming height calculation)
+ if [ "$__var_height" -o "$__var_rows" ]; then
+ # Add a row for visual aid if using Xdialog(1)
+ [ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
+ fi
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add rows to height
+ if [ "$USE_XDIALOG" ]; then
+ __height_menu_with_help_size=$((
+ $__height_menu_with_help_size + $__rows + 8 ))
+ else
+ __height_menu_with_help_size=$((
+ $__height_menu_with_help_size + $__rows + 4 ))
+ fi
+ setvar "$__var_height" $__height_menu_with_help_size
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ # The sum total between the longest tag-length and the
+ # longest item-length should be used to bump menu width
+ local __n=$(( $__longest_tag + $__longest_item + 10 ))
+ [ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_menu_with_help_size ] &&
+ __width_menu_with_help_size=$__n
+
+ # Update width for help text if using Xdialog(1)
+ if [ "$USE_XDIALOG" ]; then
+ __n=$(( $__longest_help + 10 ))
+ __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_menu_with_help_size ] &&
+ __width_menu_with_help_size=$__n
+ fi
+
+ setvar "$__var_width" $__width_menu_with_help_size
+ fi
+
+ # Store adjusted rows if desired
+ [ "$__var_rows" ] && setvar "$__var_rows" $__rows
+
+ # Constrain height, width, and rows to sensible minimum/maximum values
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] || f_dialog_menu_constrain \
+ "$__var_height" "$__var_width" "$__var_rows" "$__prompt"
+}
+
+# f_dialog_radiolist_size [-n] $var_height $var_width $var_rows \
+# $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 three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the radio list itself (comprised of tag/item/status triplets).
+# The optimal height, width, and rows for the described widget (not exceeding
+# the actual terminal height or width) is stored in $var_height, $var_width,
+# and $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_radiolist_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2" __var_rows="$3"
+ local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
+ shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
+ return $FAILURE
+
+ # Calculate height/width of infobox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_rlist_size __width_rlist_size
+ f_dialog_infobox_size -n \
+ "${__var_height:+__height_rlist_size}" \
+ "${__var_width:+__width_rlist_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ #
+ # Always process the menu-item arguments to get the longest tag-length,
+ # longest item-length (both used to bump the width), and the number of
+ # rows (used to bump the height).
+ #
+ local __longest_tag=0 __longest_item=0 __rows_rlist_size=0
+ while [ $# -ge 3 ]; do
+ local __tag="$1" __item="$2"
+ shift 3 # tag/item/status
+ [ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
+ [ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
+ __rows_rlist_size=$(( $__rows_rlist_size + 1 ))
+ done
+
+ # Adjust rows early (for up-coming height calculation)
+ if [ "$__var_height" -o "$__var_rows" ]; then
+ # Add a row for visual aid if using Xdialog(1)
+ [ "$USE_XDIALOG" ] &&
+ __rows_rlist_size=$(( $__rows_rlist_size + 1 ))
+ fi
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add rows to height
+ if [ "$USE_XDIALOG" ]; then
+ __height_rlist_size=$((
+ $__height_rlist_size + $__rows_rlist_size + 7
+ ))
+ else
+ __height_rlist_size=$((
+ $__height_rlist_size + $__rows_rlist_size + 4
+ ))
+ fi
+ setvar "$__var_height" $__height_rlist_size
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ # Sum total between longest tag-length, longest item-length,
+ # and radio-button width should be used to bump menu width
+ local __n=$(( $__longest_tag + $__longest_item + 13 ))
+ [ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_rlist_size ] && __width_rlist_size=$__n
+
+ setvar "$__var_width" $__width_rlist_size
+ fi
+
+ # Store adjusted rows if desired
+ [ "$__var_rows" ] && setvar "$__var_rows" $__rows_rlist_size
+
+ # Constrain height, width, and rows to sensible minimum/maximum values
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] || f_dialog_menu_constrain \
+ "$__var_height" "$__var_width" "$__var_rows" "$__prompt"
+}
+
+# f_dialog_checklist_size [-n] $var_height $var_width $var_rows \
+# $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
+# `--checklist' boxes sensibly.
+#
+# This function helps solve this issue by taking three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the check list itself (comprised of tag/item/status triplets).
+# The optimal height, width, and rows for the described widget (not exceeding
+# the actual terminal height or width) is stored in $var_height, $var_width,
+# and $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_checklist_size()
+{
+ f_dialog_radiolist_size "$@"
+}
+
+# f_dialog_radiolist_with_help_size [-n] $var_height $var_width $var_rows \
+# $title $backtitle $prompt $hline \
+# $tag1 $item1 $status1 $help1 \
+# $tag2 $item2 $status2 $help2 ...
+#
+# 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 three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the radio list itself (comprised of tag/item/status/help
+# quadruplets). The optimal height, width, and rows for the described widget
+# (not exceeding the actual terminal height or width) is stored in $var_height,
+# $var_width, and $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_radiolist_with_help_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2" __var_rows="$3"
+ local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
+ shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
+ return $FAILURE
+
+ # Calculate height/width of infobox (adjusted/constrained below)
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_rlist_with_help_size __width_rlist_with_help_size
+ f_dialog_infobox_size -n \
+ "${__var_height:+__height_rlist_with_help_size}" \
+ "${__var_width:+__width_rlist_with_help_size}" \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ #
+ # Always process the menu-item arguments to get the longest tag-length,
+ # longest item-length, longest help-length (help-length only considered
+ # if using Xdialog(1), as it places the help string in the widget) --
+ # all used to bump the width -- and the number of rows (used to bump
+ # the height).
+ #
+ local __longest_tag=0 __longest_item=0 __longest_help=0
+ local __rows_rlist_with_help_size=0
+ while [ $# -ge 4 ]; do
+ local __tag="$1" __item="$2" __status="$3" __help="$4"
+ shift 4 # tag/item/status/help
+ [ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
+ [ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
+ [ ${#__help} -gt $__longest_help ] && __longest_help=${#__help}
+ __rows_rlist_with_help_size=$((
+ $__rows_rlist_with_help_size + 1
+ ))
+ done
+
+ # Adjust rows early (for up-coming height calculation)
+ if [ "$__var_height" -o "$__var_rows" ]; then
+ # Add a row for visual aid if using Xdialog(1)
+ [ "$USE_XDIALOG" ] &&
+ __rows_rlist_with_help_size=$((
+ $__rows_rlist_with_help_size + 1
+ ))
+ fi
+
+ # Adjust height if desired
+ if [ "$__var_height" ]; then
+ # Add rows to height
+ if [ "$USE_XDIALOG" ]; then
+ __height_rlist_with_help_size=$((
+ $__height_rlist_with_help_size +
+ $__rows_rlist_with_help_size + 7
+ ))
+ else
+ __height_rlist_with_help_size=$((
+ $__height_rlist_with_help_size +
+ $__rows_rlist_with_help_size + 4
+ ))
+ fi
+ setvar "$__var_height" $__height
+ fi
+
+ # Adjust width if desired
+ if [ "$__var_width" ]; then
+ # Sum total between longest tag-length, longest item-length,
+ # and radio-button width should be used to bump menu width
+ local __n=$(( $__longest_tag + $__longest_item + 13 ))
+ [ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_rlist_with_help_size ] &&
+ __width_rlist_with_help_size=$__n
+
+ # Update width for help text if using Xdialog(1)
+ if [ "$USE_XDIALOG" ]; then
+ __n=$(( $__longest_help + 10 ))
+ __n=$(( $__n + $__n / 6 )) # plus 16.6%
+ [ $__n -gt $__width_rlist_with_help_size ] &&
+ __width_rlist_with_help_size=$__n
+ fi
+
+ setvar "$__var_width" $__width_rlist_with_help_size
+ fi
+
+ # Store adjusted rows if desired
+ [ "$__var_rows" ] && setvar "$__var_rows" $__rows_rlist_with_help_size
+
+ # Constrain height, width, and rows to sensible minimum/maximum values
+ # Return success if no-constrain, else return status from constrain
+ [ ! "$__constrain" ] || f_dialog_menu_constrain \
+ "$__var_height" "$__var_width" "$__var_rows" "$__prompt"
+}
+
+# f_dialog_checklist_with_help_size [-n] $var_height $var_width $var_rows \
+# $title $backtitle $prompt $hline \
+# $tag1 $item1 $status1 $help1 \
+# $tag2 $item2 $status2 $help2 ...
+#
+# Not all versions of dialog(1) perform auto-sizing of the width and height of
+# `--checklist' boxes sensibly.
+#
+# This function helps solve this issue by taking three sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height, width, and rows. The second set of arguments
+# are the title, backtitle, prompt, and hline. The [optional] third set of
+# arguments are the check list itself (comprised of tag/item/status/help
+# quadruplets). The optimal height, width, and rows for the described widget
+# (not exceeding the actual terminal height or width) is stored in $var_height,
+# $var_width, and $var_rows (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
+# and $var_rows) are not constrained to minimum/maximum values.
+#
+f_dialog_checklist_with_help_size()
+{
+ f_dialog_radiolist_with_help_size "$@"
+}
+
+# f_dialog_calendar_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are the
+# title, backtitle, prompt, and [optionally] hline. The optimal height and
+# width for the described widget (not exceeding the actual terminal height or
+# width) is stored in $var_height and $var_width (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+f_dialog_calendar_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ #
+ # Obtain/Adjust minimum and maximum thresholds
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ #
+ local __max_height_cal_size __max_width_cal_size
+ f_dialog_max_size __max_height_cal_size __max_width_cal_size
+ __max_width_cal_size=$(( $__max_width_cal_size - 2 ))
+ # the calendar box will refuse to display if too wide
+ local __min_width
+ if [ "$USE_XDIALOG" ]; then
+ __min_width=55
+ else
+ __min_width=40
+ __max_height_cal_size=$((
+ $__max_height_cal_size - $DIALOG_CALENDAR_HEIGHT ))
+ # When using dialog(1), we can't predict whether the user has
+ # disabled shadow's in their `$HOME/.dialogrc' file, so we'll
+ # subtract one for the potential shadow around the widget
+ __max_height_cal_size=$(( $__max_height_cal_size - 1 ))
+ fi
+
+ # Calculate height if desired
+ if [ "$__var_height" ]; then
+ local __height
+ __height=$( echo "$__prompt" | f_number_of_lines )
+
+ if [ "$USE_XDIALOG" ]; then
+ # Add height to accomodate for embedded calendar widget
+ __height=$(( $__height + $DIALOG_CALENDAR_HEIGHT - 1 ))
+
+ # Also, bump height if backtitle is enabled
+ if [ "$__btitle" ]; then
+ local __n
+ __n=$( echo "$__btitle" | f_number_of_lines )
+ __height=$(( $__height + $__n + 2 ))
+ fi
+ else
+ [ "$__prompt" ] && __height=$(( $__height + 1 ))
+ fi
+
+ # Enforce maximum height, unless `-n' was passed
+ [ "$__constrain" -a $__height -gt $__max_height_cal_size ] &&
+ __height=$__max_height_cal_size
+
+ setvar "$__var_height" $__height
+ fi
+
+ # Calculate width if desired
+ if [ "$__var_width" ]; then
+ # NOTE: Function name appended to prevent __var_{height,width}
+ # values from becoming local (and thus preventing setvar
+ # from working).
+ local __width_cal_size
+ f_dialog_infobox_size -n "" __width_cal_size \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ # Enforce minimum/maximum width, unless `-n' was passed
+ if [ "$__constrain" ]; then
+ if [ $__width_cal_size -lt $__min_width ]; then
+ __width_cal_size=$__min_width
+ elif [ $__width_cal_size -gt $__max_width_cal_size ]
+ then
+ __width_cal_size=$__max_width_size
+ fi
+ fi
+
+ setvar "$__var_width" $__width_cal_size
+ fi
+
+ return $SUCCESS
+}
+
+# f_dialog_timebox_size [-n] $var_height $var_width \
+# $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 two sets of sequential
+# arguments. The first set of arguments are the variable names to use when
+# storing the calculated height and width. The second set of arguments are the
+# title, backtitle, prompt, and [optionally] hline. The optional height and
+# width for the described widget (not exceeding the actual terminal height or
+# width) is stored in $var_height and $var_width (respectively).
+#
+# If the first argument is `-n', the calculated sizes ($var_height and
+# $var_width) are not constrained to minimum/maximum values.
+#
+# Newline character sequences (``\n'') in $prompt are expanded as-is done by
+# dialog(1).
+#
+f_dialog_timebox_size()
+{
+ local __constrain=1
+ [ "$1" = "-n" ] && __constrain= && shift 1 # -n
+ local __var_height="$1" __var_width="$2"
+ local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
+
+ # Return unless at least one size aspect has been requested
+ [ "$__var_height" -o "$__var_width" ] || return $FAILURE
+
+ #
+ # Obtain/Adjust minimum and maximum thresholds
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ #
+ local __max_height_tbox_size __max_width_tbox_size
+ f_dialog_max_size __max_height_tbox_size __max_width_tbox_size
+ __max_width_tbox_size=$(( $__max_width_tbox_size - 2 ))
+ # the timebox widget refuses to display if too wide
+ local __min_width
+ if [ "$USE_XDIALOG" ]; then
+ __min_width=40
+ else
+ __min_width=20
+ __max_height_tbox_size=$(( \
+ $__max_height_tbox_size - $DIALOG_TIMEBOX_HEIGHT ))
+ # When using dialog(1), we can't predict whether the user has
+ # disabled shadow's in their `$HOME/.dialogrc' file, so we'll
+ # subtract one for the potential shadow around the widget
+ __max_height_tbox_size=$(( $__max_height_tbox_size - 1 ))
+ fi
+
+ # Calculate height if desired
+ if [ "$__var_height" -a "$USE_XDIALOG" ]; then
+ # When using Xdialog(1), the height seems to have
+ # no effect. All values provide the same results.
+ setvar "$__var_height" 0 # autosize
+ elif [ "$__var_height" ]; then
+ local __height
+ __height=$( echo "$__prompt" | f_number_of_lines )
+ __height=$(( $__height ${__prompt:++1} + 1 ))
+
+ # Enforce maximum height, unless `-n' was passed
+ [ "$__constrain" -a $__height -gt $__max_height_tbox_size ] &&
+ __height=$__max_height_tbox_size
+
+ setvar "$__var_height" $__height
+ fi
+
+ # Calculate width if desired
+ if [ "$__var_width" ]; then
+ # NOTE: Function name appended to prevent __var_{height,width}
+ # values from becoming local (and thus preventing setvar
+ # from working).
+ local __width_tbox_size
+ f_dialog_infobox_size -n "" __width_tbox_size \
+ "$__title" "$__btitle" "$__prompt" "$__hline"
+
+ # Enforce the minimum width for displaying the timebox
+ if [ "$__constrain" ]; then
+ if [ $__width_tbox_size -lt $__min_width ]; then
+ __width_tbox_size=$__min_width
+ elif [ $__width_tbox_size -ge $__max_width_tbox_size ]
+ then
+ __width_tbox_size=$__max_width_tbox_size
+ fi
+ fi
+
+ setvar "$__var_width" $__width_tbox_size
+ fi
+
+ return $SUCCESS
+}
+
+############################################################ CLEAR FUNCTIONS
+
+# f_dialog_clear
+#
+# Clears any/all previous dialog(1) displays.
+#
+f_dialog_clear()
+{
+ $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 f_dialog_clear) is called.
+#
+f_dialog_info()
+{
+ local info_text="$*" height width
+ f_dialog_infobox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$info_text"
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ ${USE_XDIALOG:+--ignore-eof} \
+ ${USE_XDIALOG:+--no-buttons} \
+ --infobox "$info_text" $height $width
+}
+
+# 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.
+#
+# To open an Xdialog(1) infobox that does not disappear until expeclitly dis-
+# missed, use the following:
+#
+# f_xdialog_info "$info_text" < /dev/tty &
+# pid=$!
+# # Perform some lengthy actions
+# kill $pid
+#
+# NB: Check $USE_XDIALOG if you need to support both dialog(1) and Xdialog(1).
+#
+f_xdialog_info()
+{
+ local info_text="$*" height width
+ f_dialog_infobox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$info_text"
+ exec $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --no-close --no-buttons \
+ --infobox "$info_text" $height $width \
+ -1 # timeout of -1 means abort when EOF on stdin
+}
+
+############################################################ PAUSE FUNCTIONS
+
+# f_dialog_pause $msg_text $duration [$hline]
+#
+# Display a message in a widget with a progress bar that runs backward for
+# $duration seconds.
+#
+f_dialog_pause()
+{
+ local pause_text="$1" duration="$2" hline="$3" height width
+ f_isinteger "$duration" || return $FAILURE
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$pause_text" "$hline"
+ if [ "$USE_XDIALOG" ]; then
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --ok-label "$msg_skip" \
+ --cancel-label "$msg_cancel" \
+ ${noCancel:+--no-cancel} \
+ --timeout "$duration" \
+ --yesno "$pause_text" \
+ $height $width
+ else
+ [ $duration -gt 0 ] && duration=$(( $duration - 1 ))
+ [ $duration -gt 1 ] && duration=$(( $duration - 1 ))
+ height=$(( $height + 3 )) # Add height for progress bar
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_skip" \
+ --cancel-label "$msg_cancel" \
+ ${noCancel:+--no-cancel} \
+ --pause "$pause_text" \
+ $height $width "$duration"
+ fi
+}
+
+# f_dialog_pause_no_cancel $msg_text $duration [$hline]
+#
+# Display a message in a widget with a progress bar that runs backward for
+# $duration seconds. No cancel button is provided. Always returns success.
+#
+f_dialog_pause_no_cancel()
+{
+ noCancel=1 f_dialog_pause "$@"
+ return $SUCCESS
+}
+
+############################################################ MSGBOX FUNCTIONS
+
+# f_dialog_msgbox $msg_text [$hline]
+#
+# 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()
+{
+ local msg_text="$1" hline="$2" height width
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_ok" \
+ --msgbox "$msg_text" $height $width
+}
+
+############################################################ TEXTBOX FUNCTIONS
+
+# f_dialog_textbox $file
+#
+# Display the contents of $file (or an error if $file does not exist, etc.) in
+# a dialog(1) textbox (which has a scrollable region for the text). The textbox
+# 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_textbox()
+{
+ local file="$1"
+ local contents height width retval
+
+ contents=$( cat "$file" 2>&1 )
+ retval=$?
+
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$contents"
+
+ if [ $retval -eq $SUCCESS ]; then
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --exit-label "$msg_ok" \
+ --no-cancel \
+ --textbox "$file" $height $width
+ else
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --ok-label "$msg_ok" \
+ --msgbox "$contents" $height $width
+ fi
+}
+
+############################################################ YESNO FUNCTIONS
+
+# f_dialog_yesno $msg_text [$hline]
+#
+# 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()
+{
+ local msg_text="$1" height width
+ local hline="${2-$hline_arrows_tab_enter}"
+
+ f_interactive || return 0 # If non-interactive, return YES all the time
+
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
+
+ if [ "$USE_XDIALOG" ]; then
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_yes" \
+ --cancel-label "$msg_no" \
+ --yesno "$msg_text" $height $width
+ else
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --yes-label "$msg_yes" \
+ --no-label "$msg_no" \
+ --yesno "$msg_text" $height $width
+ fi
+}
+
+# f_dialog_noyes $msg_text [$hline]
+#
+# 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()
+{
+ local msg_text="$1" height width
+ local hline="${2-$hline_arrows_tab_enter}"
+
+ f_interactive || return 1 # If non-interactive, return NO all the time
+
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
+
+ if [ "$USE_XDIALOG" ]; then
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --default-no \
+ --ok-label "$msg_yes" \
+ --cancel-label "$msg_no" \
+ --yesno "$msg_text" $height $width
+ else
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --defaultno \
+ --yes-label "$msg_yes" \
+ --no-label "$msg_no" \
+ --yesno "$msg_text" $height $width
+ fi
+}
+
+############################################################ INPUT FUNCTIONS
+
+# f_dialog_inputstr_store [-s] $text
+#
+# Store some text from a dialog(1) inputbox to be retrieved later by
+# f_dialog_inputstr_fetch(). If the first argument is `-s', the text is
+# sanitized before being stored.
+#
+f_dialog_inputstr_store()
+{
+ local sanitize=
+ [ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
+ local text="$1"
+
+ # Sanitize the line before storing it if desired
+ [ "$sanitize" ] && f_dialog_line_sanitize text
+
+ setvar DIALOG_INPUTBOX_$$ "$text"
+}
+
+# f_dialog_inputstr_fetch [$var_to_set]
+#
+# Obtain the inputstr entered by the user from the most recently displayed
+# dialog(1) inputbox (previously stored with f_dialog_inputstr_store() above).
+# If $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+f_dialog_inputstr_fetch()
+{
+ local __var_to_set="$1" __cp
+
+ debug= f_getvar DIALOG_INPUTBOX_$$ "${__var_to_set:-__cp}" # get data
+ setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive
+
+ # Return the line on standard-out if desired
+ [ "$__var_to_set" ] || echo "$__cp"
+
+ return $SUCCESS
+}
+
+# f_dialog_input $var_to_set $prompt [$init [$hline]]
+#
+# Prompt the user with a dialog(1) inputbox to enter some value. The inputbox
+# remains until the the user presses ENTER or ESC, or otherwise ends the
+# editing session (by selecting `Cancel' for example).
+#
+# If the user presses ENTER, the exit status is zero (success), otherwise if
+# the user presses ESC the exit status is 255, or if the user chose Cancel, the
+# exit status is instead 1.
+#
+# NOTE: The hline should correspond to the type of data you want from the user.
+# NOTE: Should not be used to edit multiline values.
+#
+f_dialog_input()
+{
+ local __var_to_set="$1" __prompt="$2" __init="$3" __hline="$4"
+
+ # NOTE: Function name appended to prevent __var_{height,width} values
+ # from becoming local (and thus preventing setvar from working).
+ local __height_input __width_input
+ f_dialog_inputbox_size __height_input __width_input \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" \
+ "$__prompt" "$__init" "$__hline"
+
+ local __opterm="--"
+ [ "$USE_XDIALOG" ] && __opterm=
+
+ local __dialog_input
+ __dialog_input=$(
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$__hline" \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --inputbox "$__prompt" \
+ $__height_input $__width_input \
+ $__opterm "$__init" \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local __retval=$?
+
+ # Remove warnings and leading/trailing whitespace from user input
+ f_dialog_line_sanitize __dialog_input
+
+ setvar "$__var_to_set" "$__dialog_input"
+ return $__retval
+}
+
+############################################################ MENU FUNCTIONS
+
+# f_dialog_menutag_store [-s] $text
+#
+# Store some text from a dialog(1) menu to be retrieved later by
+# f_dialog_menutag_fetch(). If the first argument is `-s', the text is
+# sanitized before being stored.
+#
+f_dialog_menutag_store()
+{
+ local sanitize=
+ [ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
+ local text="$1"
+
+ # Sanitize the menutag before storing it if desired
+ [ "$sanitize" ] && f_dialog_data_sanitize text
+
+ setvar DIALOG_MENU_$$ "$text"
+}
+
+# f_dialog_menutag_fetch [$var_to_set]
+#
+# Obtain the menutag chosen by the user from the most recently displayed
+# dialog(1) menu (previously stored with f_dialog_menutag_store() above). If
+# $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+f_dialog_menutag_fetch()
+{
+ local __var_to_set="$1" __cp
+
+ debug= f_getvar DIALOG_MENU_$$ "${__var_to_set:-__cp}" # get the data
+ setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive
+
+ # Return the data on standard-out if desired
+ [ "$__var_to_set" ] || echo "$__cp"
+
+ return $SUCCESS
+}
+
+# f_dialog_menuitem_store [-s] $text
+#
+# Store the item from a dialog(1) menu (see f_dialog_menutag2item()) to be
+# retrieved later by f_dialog_menuitem_fetch(). If the first argument is `-s',
+# the text is sanitized before being stored.
+#
+f_dialog_menuitem_store()
+{
+ local sanitize=
+ [ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
+ local text="$1"
+
+ # Sanitize the menuitem before storing it if desired
+ [ "$sanitize" ] && f_dialog_data_sanitize text
+
+ setvar DIALOG_MENUITEM_$$ "$text"
+}
+
+# f_dialog_menuitem_fetch [$var_to_set]
+#
+# Obtain the menuitem chosen by the user from the most recently displayed
+# dialog(1) menu (previously stored with f_dialog_menuitem_store() above). If
+# $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+f_dialog_menuitem_fetch()
+{
+ local __var_to_set="$1" __cp
+
+ debug= f_getvar DIALOG_MENUITEM_$$ "${__var_to_set:-__cp}" # get data
+ setvar DIALOG_MENUITEM_$$ "" # scrub memory in case data was sensitive
+
+ # Return the data on standard-out if desired
+ [ "$__var_to_set" ] || echo "$__cp"
+
+ return $SUCCESS
+}
+
+# f_dialog_default_store [-s] $text
+#
+# Store some text to be used later as the --default-item argument to dialog(1)
+# (or Xdialog(1)) for --menu, --checklist, and --radiolist widgets. Retrieve
+# the text later with f_dialog_menutag_fetch(). If the first argument is `-s',
+# the text is sanitized before being stored.
+#
+f_dialog_default_store()
+{
+ local sanitize=
+ [ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
+ local text="$1"
+
+ # Sanitize the defaulitem before storing it if desired
+ [ "$sanitize" ] && f_dialog_data_sanitize text
+
+ setvar DEFAULTITEM_$$ "$text"
+}
+
+# f_dialog_default_fetch [$var_to_set]
+#
+# Obtain text to be used with the --default-item argument of dialog(1) (or
+# Xdialog(1)) (previously stored with f_dialog_default_store() above). If
+# $var_to_set is NULL or missing, output is printed to stdout (which is less
+# recommended due to performance degradation; in a loop for example).
+#
+f_dialog_default_fetch()
+{
+ local __var_to_set="$1" __cp
+
+ debug= f_getvar DEFAULTITEM_$$ "${__var_to_set:-__cp}" # get the data
+ setvar DEFAULTITEM_$$ "" # scrub memory in case data was sensitive
+
+ # Return the data on standard-out if desired
+ [ "$__var_to_set" ] || echo "$__cp"
+
+ 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
+}
+
+# f_dialog_menutag2help $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 help associated with said tag (item 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_menutag2help()
+{
+ local tag="$1" tagn help
+ shift 1 # tag
+
+ while [ $# -gt 0 ]; do
+ tagn="$1"
+ help="$3"
+ shift 3 # tagn/item/help
+
+ if [ "$tag" = "$tagn" ]; then
+ echo "$help"
+ return $SUCCESS
+ fi
+ 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.
+#
+# Also reads ~/.dialogrc for the following information:
+#
+# NO_SHADOW Either NULL or Non-NULL. If use_shadow is OFF (case-
+# insensitive) in ~/.dialogrc this is set to "1" (otherwise
+# unset).
+#
+f_dialog_init()
+{
+ local funcname=f_dialog_init
+
+ DIALOG_SELF_INITIALIZE=
+ USE_DIALOG=1
+
+ #
+ # Clone terminal stdout so we can redirect to it from within sub-shells
+ #
+ eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1
+
+ #
+ # Add `-S' and `-X' to the list of standard arguments supported by all
+ #
+ case "$GETOPTS_STDARGS" in
+ *SX*) : good ;; # already present
+ *) GETOPTS_STDARGS="${GETOPTS_STDARGS}SX"
+ esac
+
+ #
+ # Process stored command-line arguments
+ #
+ # NB: Using backticks instead of $(...) for portability since Linux
+ # bash(1) balks at the right parentheses encountered in the case-
+ # statement (incorrectly interpreting it as the close of $(...)).
+ #
+ f_dprintf "f_dialog_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \
+ "$ARGV" "$GETOPTS_STDARGS"
+ SECURE=`set -- $ARGV
+ OPTIND=1
+ while getopts \
+ "$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" \
+ flag > /dev/null; do
+ case "$flag" in
+ S) echo 1 ;;
+ esac
+ done
+ ` # END-BACKTICK
+ USE_XDIALOG=`set -- $ARGV
+ OPTIND=1
+ while getopts \
+ "$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" \
+ flag > /dev/null; do
+ case "$flag" in
+ S|X) echo 1 ;;
+ esac
+ done
+ ` # END-BACKTICK
+ f_dprintf "f_dialog_init: SECURE=[%s] USE_XDIALOG=[%s]" \
+ "$SECURE" "$USE_XDIALOG"
+
+ #
+ # Process `-X' command-line option
+ #
+ [ "$USE_XDIALOG" ] && DIALOG=Xdialog USE_DIALOG=
+
+ #
+ # Sanity check, or die gracefully
+ #
+ if ! f_have $DIALOG; then
+ unset USE_XDIALOG
+ local failed_dialog="$DIALOG"
+ DIALOG=dialog
+ f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
+ fi
+
+ #
+ # Read ~/.dialogrc (unless using Xdialog(1)) for properties
+ #
+ if [ -f ~/.dialogrc -a ! "$USE_XDIALOG" ]; then
+ eval "$(
+ awk -v param=use_shadow -v expect=OFF \
+ -v set="NO_SHADOW=1" '
+ !/^[[:space:]]*(#|$)/ && \
+ tolower($1) ~ "^"param"(=|$)" && \
+ /[^#]*=/ {
+ sub(/^[^=]*=[[:space:]]*/, "")
+ if ( toupper($1) == expect ) print set";"
+ }' ~/.dialogrc
+ )"
+ 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 )
+ local 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
+ local maxsize
+ if ! f_eval_catch -dk maxsize $funcname "$DIALOG" \
+ 'LANG= LC_ALL= %s --print-maxsize' "$DIALOG"
+ then
+ # Xdialog(1) failed, fall back to dialog(1)
+ unset USE_XDIALOG
+
+ # Display the error message produced by Xdialog(1)
+ local height width
+ f_dialog_buttonbox_size height width \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$maxsize"
+ dialog \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --ok-label "$msg_ok" \
+ --msgbox "$maxsize" $height $width
+ exit $FAILURE
+ fi
+
+ XDIALOG_MAXSIZE=$(
+ set -- ${maxsize##*:}
+
+ height=${1%,}
+ width=$2
+
+ echo $height $width
+ )
+ 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
+ local _DIALOG_TITLE="$DIALOG_TITLE"
+ DIALOG_TITLE="$DIALOG_BACKTITLE"
+ DIALOG_BACKTITLE="$_DIALOG_TITLE"
+ fi
+
+ f_dprintf "f_dialog_init: dialog(1) API initialized."
+}
+
+############################################################ MAIN
+
+#
+# Self-initialize unless requested otherwise
+#
+f_dprintf "%s: DIALOG_SELF_INITIALIZE=[%s]" \
+ dialog.subr "$DIALOG_SELF_INITIALIZE"
+case "$DIALOG_SELF_INITIALIZE" in
+""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
+*) f_dialog_init
+esac
+
+f_dprintf "%s: Successfully loaded." dialog.subr
+
+fi # ! $_DIALOG_SUBR
diff --git a/usr.sbin/bsdconfig/share/geom.subr b/usr.sbin/bsdconfig/share/geom.subr
new file mode 100644
index 0000000..912a5c7
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/geom.subr
@@ -0,0 +1,430 @@
+if [ ! "$_GEOM_SUBR" ]; then _GEOM_SUBR=1
+#
+# Copyright (c) 2012-2014 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." geom.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/struct.subr
+
+############################################################ GLOBALS
+
+NGEOM_CLASSES=0 # Set by f_geom_get_all()/f_geom_reset()
+
+#
+# GEOM classes for use with f_geom_find()
+#
+# NB: Since $GEOM_CLASS_ANY is the NULL string, make sure you quote it whenever
+# you put arguments after it.
+#
+setvar GEOM_CLASS_ANY "any"
+setvar GEOM_CLASS_DEV "DEV"
+setvar GEOM_CLASS_DISK "DISK"
+setvar GEOM_CLASS_ELI "ELI"
+setvar GEOM_CLASS_FD "FD"
+setvar GEOM_CLASS_LABEL "LABEL"
+setvar GEOM_CLASS_MD "MD"
+setvar GEOM_CLASS_NOP "NOP"
+setvar GEOM_CLASS_PART "PART"
+setvar GEOM_CLASS_RAID "RAID"
+setvar GEOM_CLASS_SWAP "SWAP"
+setvar GEOM_CLASS_VFS "VFS"
+setvar GEOM_CLASS_ZFS_VDEV "ZFS::VDEV"
+setvar GEOM_CLASS_ZFS_ZVOL "ZFS::ZVOL"
+
+#
+# GEOM structure definitions
+#
+f_struct_define GEOM_CLASS \
+ id name ngeoms
+f_struct_define GEOM_GEOM \
+ id class_ref config name nconsumers nproviders rank
+ # Also consumerN where N is 1 through nconsumers
+ # Also providerN where N is 1 through nproviders
+f_struct_define GEOM_CONSUMER \
+ id geom_ref config mode provider_ref
+f_struct_define GEOM_PROVIDER \
+ id geom_ref config mode name mediasize
+
+# The config property of GEOM_GEOM struct is defined as this
+f_struct_define GEOM_GEOM_CONFIG \
+ entries first fwheads fwsectors last modified scheme state
+
+# The config property of GEOM_PROVIDER struct is defined as this
+f_struct_define GEOM_PROVIDER_CONFIG \
+ descr file fwheads fwsectors ident length type unit
+
+#
+# Default behavior is to call f_geom_get_all() automatically when loaded.
+#
+: ${GEOM_SELF_SCAN_ALL=1}
+
+############################################################ FUNCTIONS
+
+# f_geom_get_all
+#
+# Parse sysctl(8) `kern.geom.confxml' data into a series of structs. GEOM
+# classes are at the top of the heirarchy and are stored as numbered structs
+# from 1 to $NGEOM_CLASSES (set by this function) named `geom_class_C'. GEOM
+# objects within each class are stored as numbered structs from 1 to `ngeoms'
+# (a property of the GEOM class struct) named `geom_class_C_geom_N' (where C
+# is the class number and N is the geom number).
+#
+# Use the function f_geom_find() to get a list of geoms (execute without
+# arguments) or find specific geoms by class or name.
+#
+f_geom_get_all()
+{
+ eval "$( sysctl -n kern.geom.confxml | awk '
+ BEGIN {
+ struct_count["class"] = 0
+ struct_count["geom"] = 0
+ struct_count["consumer"] = 0
+ struct_count["provider"] = 0
+ }
+ ############################################### FUNCTIONS
+ function set_value(prop, value)
+ {
+ if (!struct_stack[cur_struct]) return
+ printf "%s set %s \"%s\"\n",
+ struct_stack[cur_struct], prop, value
+ }
+ function create(type, id)
+ {
+ if (struct = created[type "_" id])
+ print "f_struct_free", struct
+ else {
+ struct = struct_stack[cur_struct]
+ struct = struct ( struct ? "" : "geom" )
+ struct = struct "_" type "_" ++struct_count[type]
+ created[type "_" id] = struct
+ }
+ print "debug= f_struct_new GEOM_" toupper(type), struct
+ cur_struct++
+ struct_stack[cur_struct] = struct
+ type_stack[cur_struct] = type
+ set_value("id", id)
+ }
+ function create_config()
+ {
+ struct = struct_stack[cur_struct]
+ struct = struct ( struct ? "" : "geom" )
+ struct = struct "_config"
+ set_value("config", struct)
+ type = type_stack[cur_struct]
+ print "debug= f_struct_new GEOM_" toupper(type) "_CONFIG", \
+ struct
+ cur_struct++
+ struct_stack[cur_struct] = struct
+ type_stack[cur_struct] = type "_config"
+ }
+ function extract_attr(field, attr)
+ {
+ if (match(field, attr "=\"0x[[:xdigit:]]+\"")) {
+ len = length(attr)
+ return substr($2, len + 3, RLENGTH - len - 3)
+ }
+ }
+ function extract_data(type)
+ {
+ data = $0
+ sub("^[[:space:]]*<" type ">", "", data)
+ sub("</" type ">.*$", "", data)
+ return data
+ }
+ ############################################### OPENING PATTERNS
+ $1 == "<mesh>" { mesh = 1 }
+ $1 ~ /^<(class|geom)$/ && mesh {
+ prop = substr($1, 2)
+ if ((ref = extract_attr($2, "ref")) != "")
+ set_value(prop "_ref", ref)
+ else if ((id = extract_attr($2, "id")) != "")
+ create(prop, id)
+ }
+ $1 ~ /^<(consumer|provider)$/ && mesh {
+ prop = substr($1, 2)
+ if ((ref = extract_attr($2, "ref")) != "")
+ set_value(prop "_ref", ref)
+ else if ((id = extract_attr($2, "id")) != "") {
+ create(prop, id)
+ cur_struct--
+ propn = struct_count[prop]
+ set_value(prop propn, struct_stack[cur_struct+1])
+ cur_struct++
+ }
+ }
+ $1 == "<config>" && mesh { create_config() }
+ ############################################### PROPERTIES
+ $1 ~ /^<[[:alnum:]]+>/ {
+ prop = $1
+ sub(/^</, "", prop); sub(/>.*/, "", prop)
+ set_value(prop, extract_data(prop))
+ }
+ ############################################### CLOSING PATTERNS
+ $1 ~ "^</(consumer|provider|config)>$" { cur_struct-- }
+ $1 == "</geom>" {
+ set_value("nconsumers", struct_count["consumer"])
+ set_value("nproviders", struct_count["provider"])
+ cur_struct--
+ struct_count["consumer"] = 0
+ struct_count["provider"] = 0
+ }
+ $1 == "</class>" {
+ set_value("ngeoms", struct_count["geom"])
+ cur_struct--
+ struct_count["consumer"] = 0
+ struct_count["provider"] = 0
+ struct_count["geom"] = 0
+ }
+ $1 == "</mesh>" {
+ printf "NGEOM_CLASSES=%u\n", struct_count["class"]
+ delete struct_count
+ mesh = 0
+ }' )"
+}
+
+# f_geom_reset
+#
+# Reset the registered GEOM chain.
+#
+f_geom_reset()
+{
+ local classn=1 class ngeoms geomn geom
+ while [ $classn -le ${NGEOM_CLASSES:-0} ]; do
+ class=geom_class_$classn
+ $class get ngeoms ngeoms
+ geomn=1
+ while [ $geomn -le $ngeoms ]; do
+ f_struct_free ${class}_geom_$geomn
+ geomn=$(( $geomn + 1 ))
+ done
+ classn=$(( $classn + 1 ))
+ done
+ NGEOM_CLASSES=0
+}
+
+# f_geom_rescan
+#
+# Rescan all GEOMs - convenience function.
+#
+f_geom_rescan()
+{
+ f_geom_reset
+ f_geom_get_all
+}
+
+# f_geom_find $name [$type [$var_to_set]]
+#
+# Find one or more registered GEOMs by name, type, or both. Returns a space-
+# separated list of GEOMs matching the search criterion. The $type argument
+# should be the GEOM class (see $GEOM_CLASS_* variables in GLOBALS above).
+#
+# If $var_to_set is missing or NULL, the GEOM name(s) are printed to standard
+# out for capturing in a sub-shell (which is less-recommended because of
+# performance degredation; for example, when called in a loop).
+#
+f_geom_find()
+{
+ local __name="$1" __type="${2:-$GEOM_CLASS_ANY}" __var_to_set="$3"
+ local __classn=1 __class __class_name __ngeoms
+ local __geomn __geom __geom_name __found=
+ while [ $__classn -le ${NGEOM_CLASSES:-0} ]; do
+ __class=geom_class_$__classn
+ $__class get name __class_name
+ if [ "$__type" != "$GEOM_CLASS_ANY" -a \
+ "$__type" != "$__class_name" ]
+ then
+ __classn=$(( $__classn + 1 ))
+ continue
+ fi
+
+ __geomn=1
+ $__class get ngeoms __ngeoms || __ngeoms=0
+ while [ $__geomn -le $__ngeoms ]; do
+ __geom=${__class}_geom_$__geomn
+ $__geom get name __geom_name
+ [ "$__name" = "$__geom_name" -o ! "$__name" ] &&
+ __found="$__found $__geom"
+ __geomn=$(( $__geomn + 1 ))
+ done
+ __classn=$(( $__classn + 1 ))
+ done
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "${__found# }"
+ else
+ echo $__found
+ fi
+ [ "$__found" ] # Return status
+}
+
+# f_geom_find_by $prop $find [$type [$var_to_set]]
+#
+# Find GEOM-related struct where $prop of the struct is equal to $find. Returns
+# NULL or the name of the first GEOM struct to match. The $type argument should
+# be one of the following:
+#
+# NULL Find any of the below
+# class Find GEOM_CLASS struct
+# geom Find GEOM_GEOM struct
+# consumer Find GEOM_CONSUMER struct
+# provider Find GEOM_PROVIDER struct
+#
+# The $prop argument can be any property of the given type of struct. Some
+# properties are common to all types (such as id) so the $type argument is
+# optional (allowing you to return any struct whose property matches $find).
+#
+# If $var_to_set is missing or NULL, the GEOM struct name is printed to
+# standard out for capturing in a sub-shell (which is less-recommended because
+# of performance degredation; for example when called in a loop).
+#
+f_geom_find_by()
+{
+ local __prop="$1" __find="$2" __type="$3" __var_to_set="$4"
+ local __classn=1 __class __ngeoms
+ local __geomn __geom __nitems
+ local __itype __itemn __item
+ local __value __found=
+
+ if [ ! "$__prop" ]; then
+ [ "$__var_to_set" ] && setvar "$__var_to_set" ""
+ return $FAILURE
+ fi
+
+ case "$__type" in
+ "") : OK ;;
+ class|GEOM_CLASS) __type=class ;;
+ geom|GEOM_GEOM) __type=geom ;;
+ consumer|GEOM_CONSUMER) __type=consumer ;;
+ provider|GEOM_PROVIDER) __type=provider ;;
+ *)
+ [ "$__var_to_set" ] && setvar "$__var_to_set" ""
+ return $FAILURE
+ esac
+
+ while [ $__classn -le ${NGEOM_CLASSES:-0} ]; do
+ __class=geom_class_$__classn
+
+ if [ "${__type:-class}" = "class" ]; then
+ $__class get "$__prop" __value || __value=
+ [ "$__value" = "$__find" ] && __found="$__class" break
+ [ "$__type" ] && __classn=$(( $__classn + 1 )) continue
+ fi
+
+ __geomn=1
+ $__class get ngeoms __ngeoms || __ngeoms=0
+ while [ $__geomn -le $__ngeoms ]; do
+ __geom=${__class}_geom_$__geomn
+
+ if [ "${__type:-geom}" = "geom" ]; then
+ $__geom get "$__prop" __value || __value=
+ [ "$__value" = "$__find" ] &&
+ __found="$__geom" break
+ [ "$__type" ] &&
+ __geomn=$(( $__geomn + 1 )) continue
+ fi
+
+ for __itype in ${__type:-consumer provider}; do
+ $__geom get n${__itype}s __nitems || continue
+ __itemn=1
+ while [ $__itemn -le $__nitems ]; do
+ __item=${__geom}_${__itype}_$__itemn
+
+ $__item get "$__prop" __value ||
+ __value=
+ [ "$__value" = "$__find" ] &&
+ __found="$__item" break
+ __itemn=$(( $__itemn + 1 ))
+ done
+ [ "$__found" ] && break
+ done
+ [ "$__found" ] && break
+ __geomn=$(( $__geomn + 1 ))
+ done
+ [ "$__found" ] && break
+ __classn=$(( $__classn + 1 ))
+ done
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__found"
+ else
+ [ "$__found" ] && echo "$__found"
+ fi
+ [ "$__found" ] # Return status
+}
+
+# f_geom_parent $geom|$consumer|$provider|$config [$var_to_set]
+#
+# Get the GEOM class associated with one of $geom, $consumer, $provider or
+# $config.
+#
+# If $var_to_set is missing or NULL, the GEOM class name is printed to standard
+# out for capturing in a sub-shell (which is less-recommended because of
+# performance degredation; for example when called in a loop).
+#
+f_geom_parent()
+{
+ local __struct="$1" __var_to_set="$2"
+ # NB: Order of pattern matches below is important
+ case "$__struct" in
+ *_config*) __struct="${__struct%_config*}" ;;
+ *_consumer_*) __struct="${__struct%_consumer_[0-9]*}" ;;
+ *_provider_*) __struct="${__struct%_provider_[0-9]*}" ;;
+ *_geom_*) __struct="${__struct%_geom_[0-9]*}" ;;
+ *) __struct=
+ esac
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__struct"
+ else
+ echo "$__struct"
+ fi
+ f_struct "$__struct" # Return status
+}
+
+############################################################ MAIN
+
+#
+# Parse GEOM configuration unless requested otherwise
+#
+f_dprintf "%s: GEOM_SELF_SCAN_ALL=[%s]" geom.subr "$GEOM_SELF_SCAN_ALL"
+case "$GEOM_SELF_SCAN_ALL" in
+""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
+*)
+ f_geom_get_all
+ if [ "$debug" ]; then
+ debug= f_geom_find "" "$GEOM_CLASS_ANY" geoms
+ f_count ngeoms $geoms
+ f_dprintf "%s: Initialized %u geom devices in %u classes." \
+ geom.subr "$ngeoms" "$NGEOM_CLASSES"
+ unset geoms ngeoms
+ fi
+esac
+
+f_dprintf "%s: Successfully loaded." geom.subr
+
+fi # ! $_GEOM_SUBR
diff --git a/usr.sbin/bsdconfig/share/keymap.subr b/usr.sbin/bsdconfig/share/keymap.subr
new file mode 100644
index 0000000..7f2f87c
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/keymap.subr
@@ -0,0 +1,263 @@
+if [ ! "$_KEYMAP_SUBR" ]; then _KEYMAP_SUBR=1
+#
+# Copyright (c) 2013-2015 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." keymap.subr
+f_include $BSDCFG_SHARE/struct.subr
+
+############################################################ CONFIGURATION
+
+#
+# Defaults taken from usr.sbin/kbdmap/kbdmap.h
+#
+: ${DEFAULT_LANG:=en}
+: ${DEFAULT_KEYMAP_DIR:=/usr/share/syscons/keymaps}
+
+############################################################ GLOBALS
+
+KEYMAPS=
+NKEYMAPS=0
+
+# A "keymap" from kbdmap's point of view
+f_struct_define KEYMAP \
+ desc \
+ keym \
+ mark
+
+#
+# Default behavior is to call f_keymap_get_all() automatically when loaded.
+#
+: ${KEYMAP_SELF_SCAN_ALL=1}
+
+############################################################ FUNCTIONS
+
+# f_keymap_register $name $desc $keym $mark
+#
+# Register a keymap. A `structure' (see struct.subr) is created with the name
+# keymap_$name (so make sure $name contains only alpha-numeric characters or
+# the underscore, `_'). The remaining arguments after $name correspond to the
+# propertise of the `KEYMAP' structure-type (defined above).
+#
+# If not already registered, the keymap is then appended to the KEYMAPS
+# environment variable, a space-separated list of all registered keymaps.
+#
+f_keymap_register()
+{
+ local name="$1" desc="$2" keym="$3" mark="$4"
+
+ f_struct_new KEYMAP "keymap_$name" || return $FAILURE
+ keymap_$name set desc "$desc"
+ keymap_$name set keym "$keym"
+ keymap_$name set mark "$mark"
+
+ # Scan our global register to see if needs ammending
+ local k found=
+ for k in $KEYMAPS; do
+ [ "$k" = "$name" ] || continue
+ found=1 && break
+ done
+ [ "$found" ] || KEYMAPS="$KEYMAPS $name"
+
+ return $SUCCESS
+}
+
+# f_keymap_checkfile $keymap
+#
+# Check that $keymap is a readable kbdmap(5) file. Returns success if $keymap
+# is a file, is readable, and exists in $DEFAULT_KEYMAP_DIR; otherwise failure.
+# If debugging is enabled, an appropriate debug error message is printed if
+# $keymap is not available.
+#
+f_keymap_checkfile()
+{
+ local keym="$1"
+
+ # Fixup keymap if it doesn't already contain at least one `/'
+ [ "${keym#*/}" = "$keym" ] && keym="$DEFAULT_KEYMAP_DIR/$keym"
+
+ # Short-cuts
+ [ -f "$keym" -a -r "$keym" ] && return $SUCCESS
+ f_debugging || return $FAILURE
+
+ # Print an appropriate debug error message
+ if [ ! -e "$keym" ]; then
+ f_dprintf "%s: No such file or directory" "$keym"
+ elif [ ! -f "$keym" ]; then
+ f_dprintf "%s: Not a file!" "$keym"
+ elif [ ! -r "$keym" ]; then
+ f_dprintf "%s: Permission denied" "$keym"
+ fi
+
+ return $FAILURE
+}
+
+# f_keymap_get_all
+#
+# Get all keymap information for kbdmap(5) entries both in the database and
+# loosely existing in $DEFAULT_KEYMAP_DIR.
+#
+f_keymap_get_all()
+{
+ local fname=f_keymap_get_all
+ local lang="${LC_ALL:-${LC_CTYPE:-${LANG:-$DEFAULT_LANG}}}"
+ [ "$lang" = "C" ] && lang="$DEFAULT_LANG"
+
+ f_dprintf "%s: Looking for keymap files..." $fname
+ f_dialog_info "$msg_looking_for_keymap_files"
+ f_dprintf "DEFAULT_LANG=[%s]" "$DEFAULT_LANG"
+
+ eval "$( awk -F: -v lang="$lang" -v lang_default="$DEFAULT_LANG" '
+ BEGIN {
+ # en_US.ISO8859-1 -> en_..\.ISO8859-1
+ dialect = lang
+ if (length(dialect) >= 6 &&
+ substr(dialect, 3, 1) == "_")
+ dialect = substr(dialect, 1, 3) ".." \
+ substr(dialect, 6)
+ printf "f_dprintf \"dialect=[%%s]\" \"%s\";\n", dialect
+
+ # en_US.ISO8859-1 -> en
+ lang_abk = lang
+ if (length(lang_abk) >= 3 &&
+ substr(lang_abk, 3, 1) == "_")
+ lang_abk = substr(lang_abk, 1, 2)
+ printf "f_dprintf \"lang_abk=[%%s]\" \"%s\";\n",
+ lang_abk
+ }
+ function find_token(buffer, token)
+ {
+ if (split(buffer, tokens, /,/) == 0) return 0
+ found = 0
+ for (t in tokens)
+ if (token == tokens[t]) { found = 1; break }
+ return found
+ }
+ function add_keymap(desc,mark,keym)
+ {
+ marks[keym] = mark
+ name = keym
+ gsub(/[^[:alnum:]_]/, "_", name)
+ gsub(/'\''/, "'\''\\'\'\''", desc);
+ printf "f_keymap_checkfile %s && " \
+ "f_keymap_register %s '\'%s\'' %s %u\n",
+ keym, name, desc, keym, mark
+ }
+ !/^[[:space:]]*(#|$)/ {
+ sub(/^[[:space:]]*/, "", $0)
+ keym = $1
+ if (keym ~ /^(MENU|FONT)$/) next
+ lg = ($2 == "" ? lang_default : $2)
+
+ # Match the entry and store the type of match we made
+ # as the mark value (so that if we make a better match
+ # later on with a higher mark, it overwrites previous)
+
+ mark = marks[keym];
+ if (find_token(lg, lang))
+ add_keymap($3, 4, keym) # Best match
+ else if (mark <= 3 && find_token(lg, dialect))
+ add_keymap($3, 3, keym)
+ else if (mark <= 2 && find_token(lg, lang_abk))
+ add_keymap($3, 2, keym)
+ else if (mark <= 1 && find_token(lg, lang_default))
+ add_keymap($3, 1, keym)
+ else if (mark <= 0)
+ add_keymap($3, 0, keym)
+ }
+ ' "$DEFAULT_KEYMAP_DIR/INDEX.${DEFAULT_KEYMAP_DIR##*/}" )"
+
+
+ #
+ # Look for keymaps not in database
+ #
+ local direntry keym name
+ set +f # glob
+ for direntry in "$DEFAULT_KEYMAP_DIR"/*; do
+ [ "${direntry##*.}" = ".kbd" ] || continue
+ keym="${direntry##*/}"
+ f_str2varname "$keym" name
+ f_struct keymap_$name && continue
+ f_keymap_checkfile "$keym" &&
+ f_keymap_register $name "${keym%.*}" "$keym" 0
+ f_dprintf "%s: not in kbdmap(5) database" "$keym"
+ done
+
+ #
+ # Sort the items by their descriptions
+ #
+ f_dprintf "%s: Sorting keymap entries by description..." $fname
+ KEYMAPS=$(
+ for k in $KEYMAPS; do
+ echo -n "$k "
+ # NOTE: Translate '8x8' to '8x08' before sending to
+ # sort(1) so that things work out as we might expect.
+ debug= keymap_$k get desc | awk 'gsub(/8x8/,"8x08")||1'
+ done | sort -k2 | awk '{
+ printf "%s%s", (started ? " " : ""), $1; started = 1
+ }'
+ )
+
+ return $SUCCESS
+}
+
+# f_keymap_kbdcontrol $keymap
+#
+# Install keyboard map file from $keymap.
+#
+f_keymap_kbdcontrol()
+{
+ local keymap="$1"
+
+ [ "$keymap" ] || return $SUCCESS
+
+ # Fixup keymap if it doesn't already contain at least one `/'
+ [ "${keymap#*/}" = "$keymap" ] && keymap="$DEFAULT_KEYMAP_DIR/$keymap"
+
+ [ "$USE_XDIALOG" ] || kbdcontrol -l "$keymap"
+}
+
+############################################################ MAIN
+
+#
+# Scan for keymaps unless requeted otherwise
+#
+f_dprintf "%s: KEYMAP_SELF_SCAN_ALL=[%s]" keymap.subr "$KEYMAP_SELF_SCAN_ALL"
+case "$KEYMAP_SELF_SCAN_ALL" in
+""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
+*) f_keymap_get_all
+esac
+
+f_count NKEYMAPS $KEYMAPS
+f_dprintf "%s: Found %u keymap file(s)." keymap.subr $NKEYMAPS
+
+f_dprintf "%s: Successfully loaded." keymap.subr
+
+fi # ! $_KEYMAP_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/Makefile b/usr.sbin/bsdconfig/share/media/Makefile
new file mode 100644
index 0000000..135515b
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+FILESDIR= ${SHAREDIR}/bsdconfig/media
+FILES= any.subr cdrom.subr common.subr directory.subr dos.subr \
+ floppy.subr ftp.subr http.subr httpproxy.subr network.subr \
+ nfs.subr options.subr tcpip.subr ufs.subr usb.subr
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsdconfig/share/media/Makefile.depend b/usr.sbin/bsdconfig/share/media/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/bsdconfig/share/media/any.subr b/usr.sbin/bsdconfig/share/media/any.subr
new file mode 100644
index 0000000..516d2d2
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/any.subr
@@ -0,0 +1,149 @@
+if [ ! "$_MEDIA_ANY_SUBR" ]; then _MEDIA_ANY_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/any.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/cdrom.subr
+f_include $BSDCFG_SHARE/media/directory.subr
+f_include $BSDCFG_SHARE/media/dos.subr
+f_include $BSDCFG_SHARE/media/floppy.subr
+f_include $BSDCFG_SHARE/media/ftp.subr
+f_include $BSDCFG_SHARE/media/http.subr
+f_include $BSDCFG_SHARE/media/httpproxy.subr
+f_include $BSDCFG_SHARE/media/nfs.subr
+f_include $BSDCFG_SHARE/media/options.subr
+f_include $BSDCFG_SHARE/media/ufs.subr
+f_include $BSDCFG_SHARE/media/usb.subr
+f_include $BSDCFG_SHARE/struct.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+MEDIA_HELPFILE=$BSDCFG_LIBE/include/media.hlp
+
+############################################################ FUNCTIONS
+
+# f_media_get_type
+#
+# Prompt the user to select amongst the known media types (included above).
+#
+# If the user does not cancel or press Esc, invokes the f_media_set_* function
+# associated with the chosen media type. If after all that we have a struct
+# named `device_media' then success is returned, otherwise failure.
+#
+# NOTE: The f_media_set_* function should create the `device_media' struct.
+# See `struct.subr' and the above `media/*.subr' includes for more details.
+#
+f_media_get_type()
+{
+ f_dialog_title "$msg_choose_installation_media"
+ local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
+ f_dialog_title_restore
+ local prompt="$msg_choose_installation_media_description"
+ local menu_list="
+ '1 $msg_cd_dvd' '$msg_install_from_a_freebsd_cd_dvd'
+ '2 $msg_ftp' '$msg_install_from_an_ftp_server'
+ '3 $msg_http_proxy'
+ '$msg_install_from_an_ftp_server_thru_proxy'
+ '4 $msg_http_direct' '$msg_install_from_an_http_server'
+ '5 $msg_directory' '$msg_install_from_the_existing_filesystem'
+ '6 $msg_nfs' '$msg_install_over_nfs'
+ '7 $msg_dos' '$msg_install_from_a_dos_partition'
+ '8 $msg_ufs' '$msg_install_from_a_ufs_partition'
+ '9 $msg_floppy' '$msg_install_from_a_floppy_disk_set'
+ 'A $msg_usb' '$msg_install_from_a_usb_drive'
+ 'X $msg_options' '$msg_view_set_various_media_options'
+ " # END-QUOTE
+ local hline="$hline_choose_help_for_more_information_on_media_types"
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$title\" \
+ \"\$btitle\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local mtag
+ while :; do
+ mtag=$( eval $DIALOG \
+ --title \"\$title\" \
+ --backtitle \"\$btitle\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_cancel\" \
+ --help-button \
+ --help-label \"\$msg_help\" \
+ ${USE_XDIALOG:+--help \"\"} \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_data_sanitize mtag
+ f_dprintf "retval=%s mtag=[%s]" $retval "$mtag"
+
+ if [ $retval -eq $DIALOG_HELP ]; then
+ f_show_help "$MEDIA_HELPFILE"
+ continue
+ elif [ $retval -ne $DIALOG_OK ]; then
+ return $FAILURE
+ fi
+
+ case "$mtag" in
+ ?" $msg_cd_dvd") f_media_set_cdrom ;;
+ ?" $msg_ftp") f_media_set_ftp ;;
+ ?" $msg_http_proxy") f_media_set_http_proxy ;;
+ ?" $msg_http_direct") f_media_set_http ;;
+ ?" $msg_directory") f_media_set_directory ;;
+ ?" $msg_dos") f_media_set_dos ;;
+ ?" $msg_nfs") f_media_set_nfs ;;
+ ?" $msg_ufs") f_media_set_ufs ;;
+ ?" $msg_floppy") f_media_set_floppy ;;
+ ?" $msg_usb") f_media_set_usb ;;
+ ?" $msg_options")
+ f_media_options_menu
+ continue
+ ;;
+ esac
+ break
+ done
+
+ f_struct device_media || return $FAILURE
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/any.subr
+
+fi # ! $_MEDIA_ANY_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/cdrom.subr b/usr.sbin/bsdconfig/share/media/cdrom.subr
new file mode 100644
index 0000000..bbbd638
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/cdrom.subr
@@ -0,0 +1,217 @@
+if [ ! "$_MEDIA_CDROM_SUBR" ]; then _MEDIA_CDROM_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/cdrom.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+CDROM_MOUNTED=
+CDROM_PREVIOUSLY_MOUNTED=
+CDROM_INIT_QUIET=
+
+############################################################ FUNCTIONS
+
+# f_media_set_cdrom
+#
+# Return success if we both found and set the media type to be a CD.
+#
+f_media_set_cdrom()
+{
+ f_media_close
+
+ local devs ndevs
+ f_device_find "" $DEVICE_TYPE_CDROM devs
+ f_count ndevs $devs
+
+ if [ ${ndevs:=0} -eq 0 ]; then
+ f_interactive && f_show_msg "$msg_no_cd_dvd_devices_found"
+ return $FAILURE
+ elif [ $ndevs -eq 1 ]; then
+ f_struct_copy $devs device_media
+ else
+ local dev
+ local title="$msg_choose_a_cd_dvd_type"
+ local prompt="$msg_please_select_a_cd_dvd_drive"
+ local hline=
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_CDROM \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $FAILURE
+
+ f_struct_copy "$dev" device_media
+ fi
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_cdrom $device
+#
+# Initializes the CDROM media device. Returns success if able to mount the CD
+# device using mount_cd9660(8).
+#
+f_media_init_cdrom()
+{
+ local funcname=f_media_init_cdrom
+ local dev="$1" devname err
+
+ f_struct "$dev" get devname devname || return $FAILURE
+ f_dprintf "Init routine called for CDROM device. devname=[%s]" \
+ "$devname"
+
+ if [ "$CDROM_MOUNTED" ]; then
+ f_dprintf "CDROM device already mounted."
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$MOUNTPOINT" ]; then
+ f_eval_catch $funcname mkdir 'mkdir -p "%s"' "$MOUNTPOINT" ||
+ return $FAILURE
+ fi
+
+ if ! f_eval_catch -dk err $funcname mount_cd9660 \
+ 'mount_cd9660 "%s" "%s"' "$devname" "$MOUNTPOINT"
+ then
+ err="${err#mount_cd9660: }"; err="${err#$devname: }"
+ case "$err" in
+ "Device busy")
+ # Perhaps the CDROM drive is already mounted as /cdrom
+ if f_mounted /cdrom; then
+ CDROM_PREVIOUSLY_MOUNTED=1
+ MOUNTPOINT=/cdrom
+ err=
+ fi
+ ;;
+ esac
+ case "$err" in
+ "") : good ;; # no error
+ *)
+ [ "$CDROM_INIT_QUIET" ] ||
+ f_show_msg "$msg_error_mounting_device" \
+ "$devname" "$MOUNTPOINT" "$err"
+ return $FAILURE
+ esac
+ fi
+ CDROM_MOUNTED=1
+
+ : xxx # /cdrom.inf has been deprecated since 9.0-R
+
+ # No other CDROM media validation at this time
+
+ return $SUCCESS
+}
+
+# f_media_get_cdrom $device $file [$probe_type]
+#
+# Returns data from $file on a mounted CDROM device. Similar to cat(1). If
+# $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_get_cdrom()
+{
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_cdrom: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_type"
+}
+
+# f_media_shutdown_cdrom $device
+#
+# Shuts down the CDROM device. Return status should be ignored.
+#
+f_media_shutdown_cdrom()
+{
+ local funcname=f_media_shutdown_cdrom
+ local dev="$1" err
+
+ [ "$CDROM_MOUNTED" ] || return $FAILURE
+
+ if [ "$CDROM_PREVIOUSLY_MOUNTED" ]; then
+ CDROM_MOUNTED=
+ return $SUCCESS
+ fi
+
+ if ! f_eval_catch -dk err $funcname umount \
+ 'umount -f "%s"' "$MOUNTPOINT"
+ then
+ err="${err#umount: }"; err="${err#*: }"
+ f_show_msg "$msg_could_not_unmount_the_cdrom_dvd" \
+ "$MOUNTPOINT" "$err"
+ else
+ CDROM_MOUNTED=
+ fi
+}
+
+# f_media_eject_cdrom $device
+#
+# Eject the media from the CDROM device. Returns success.
+#
+f_media_eject_cdrom()
+{
+ local funcname=f_media_eject_cdrom
+ local dev="$1" name devname err
+
+ f_struct "$dev" || return $SUCCESS
+ $dev get name name || return $SUCCESS
+ $dev get devname devname || return $SUCCESS
+
+ # Don't eject labels
+ case "$name" in */*) return $SUCCESS; esac
+
+ f_dprintf "Ejecting CDROM/DVD at %s" "$devname"
+ if ! f_eval_catch -dk err $funcname cdcontrol \
+ 'cdcontrol -f "%s" eject' "$devname"
+ then
+ f_dprintf "Could not eject the CDROM/DVD from %s: %s" \
+ "$devname" "${err#cdcontrol: }"
+ fi
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/cdrom.subr
+
+fi # ! $_MEDIA_CDROM_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/common.subr b/usr.sbin/bsdconfig/share/media/common.subr
new file mode 100644
index 0000000..0bd420d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/common.subr
@@ -0,0 +1,155 @@
+if [ ! "$_MEDIA_COMMON_SUBR" ]; then _MEDIA_COMMON_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/common.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/media/any.subr
+f_include $BSDCFG_SHARE/struct.subr
+
+############################################################ GLOBALS
+
+#
+# Where to mount media
+#
+MOUNTPOINT=/dist
+
+#
+# Media probe values to use for `f_media_get_TYPE media $file $PROBE' or
+# `f_device_get device_media $file $PROBE' (where $PROBE is one of the below
+# values).
+#
+PROBE_EXIST=1
+PROBE_SIZE=2
+
+############################################################ FUNCTIONS
+
+# f_media_open
+#
+# Returms success if able to initialize the media device.
+#
+f_media_open()
+{
+ f_dprintf "f_media_open: Verifying and initiliazing media device"
+ { # Verify and initialize device media if-defined
+ f_struct device_media &&
+ f_media_verify &&
+ f_device_init device_media
+ } || return $FAILURE
+}
+
+# f_media_close
+#
+# Shuts down the media device, see f_device_shutdown() from device.subr for
+# more details.
+#
+f_media_close()
+{
+ f_dprintf "f_media_close: Shutting down media device"
+ f_struct device_media &&
+ f_device_shutdown device_media
+ f_struct_free device_media
+}
+
+# f_media_verify
+#
+# Returns success if the media device is available, and if not, prompts the
+# user to select a media type. See f_media_get_type() from media/any.subr for
+# more details.
+#
+f_media_verify()
+{
+ f_dprintf "f_media_verify: Verifying media device"
+ f_struct device_media || f_media_get_type
+}
+
+# f_media_generic_get $base $file [$probe_type]
+#
+# A generic open which follows a well-known "path" of places to look. If
+# $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_generic_get()
+{
+ local funcname=f_media_generic_get
+ local base="$1" file="$2" probe_type="$3"
+
+ local fname=f_media_generic_get
+ f_dprintf "%s: base=[%s] files=[%s] probe_type=%s" \
+ $fname "$base" "$file" "$probe_type"
+
+ local rel path
+ f_getvar $VAR_RELNAME rel
+ for path in \
+ "$base/$file" \
+ "$base/FreeBSD/$file" \
+ "$base/releases/$file" \
+ "$base/$rel/$file" \
+ ; do
+ if [ -f "$path" -a -r "$path" ]; then
+ f_dprintf "%s: file exists path=[%s]" $fname "$path"
+ if [ "$probe_type" = "$PROBE_SIZE" ]; then
+ local size
+ f_eval_catch -dk size $funcname stat \
+ 'stat -f %%z "%s"' "$path" || size=-1
+ f_isinteger "$size" || size=-1
+ echo $size
+ fi
+ [ "$probe_type" ] && return $SUCCESS
+ cat "$path"
+ return $?
+ fi
+ done
+
+ path="$base/releases/$rel/$file" # Final path to try
+ if [ -f "$path" -a -r "$path" ]; then
+ f_dprintf "%s: file exists path=[%s]" $fname "$path"
+ if [ "$probe_type" = "$PROBE_SIZE" ]; then
+ local size
+ f_eval_catch -dk size $funcname stat \
+ 'stat -f %%z "%s"' "$path" || size=-1
+ f_isinteger "$size" || size=-1
+ echo $size
+ fi
+ [ "$probe_type" ] && return $SUCCESS
+ elif [ "$probe_type" ]; then
+ [ "$probe_type" = "$PROBE_SIZE" ] && echo "-1"
+ return $FAILURE
+ fi
+ cat "$base/releases/$rel/$file" # Final path to try
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/common.subr
+
+fi # ! $_MEDIA_COMMON_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/directory.subr b/usr.sbin/bsdconfig/share/media/directory.subr
new file mode 100644
index 0000000..004cb74
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/directory.subr
@@ -0,0 +1,151 @@
+if [ ! "$_MEDIA_DIRECTORY_SUBR" ]; then _MEDIA_DIRECTORY_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/directory.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+DIRECTORY_CHECKED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_directory
+#
+# Return success if we both found and set the media type to be a local
+# directory.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_DIRECTORY_PATH
+# Path to an existing directory containing the FreeBSD
+# distribution files.
+#
+f_media_set_directory()
+{
+ local path
+
+ f_media_close
+
+ f_variable_get_value $VAR_DIRECTORY_PATH \
+ "$msg_enter_a_fully_qualified_pathname_for_the_directory"
+ f_getvar $VAR_DIRECTORY_PATH path
+ [ "$path" ] || return $FAILURE
+
+ f_struct_new DEVICE device_directory
+ device_directory set name "$path"
+ device_directory set get f_media_get_directory
+ device_directory set init f_media_init_directory
+ device_directory set shutdown f_media_shutdown_directory
+ device_directory set private "$path"
+
+ f_struct_copy device_directory device_media
+ f_struct_free device_directory
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_directory $device
+#
+# Initializes the Directory media device. Returns success if the directory path
+# both exists and is a directory.
+#
+f_media_init_directory()
+{
+ local dev="$1" path
+
+ $dev get private path || return $FAILURE
+ f_dprintf "Init routine called for Directory device. path=[%s]" \
+ "$path"
+
+ # Track whether we've been through here before (for remote filesystems
+ # mounted in the directory path, not repeating these queries saves us
+ # valuable time for slow/uncooperative links).
+ if [ "$DIRECTORY_CHECKED" ]; then
+ f_dprintf "Directory device already checked."
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$path" ]; then
+ f_show_msg "$msg_no_such_file_or_directory" \
+ "f_media_init_directory" "$path"
+ return $FAILURE
+ elif [ ! -d "$path" ]; then
+ f_show_msg "$msg_not_a_directory" \
+ "f_media_init_directory" "$path"
+ return $FAILURE
+ fi
+ DIRECTORY_CHECKED=1
+ return $SUCCESS
+}
+
+# f_media_get_directory $device $file [$probe_type]
+#
+# Returns data from $file in the existing/current filesystem. Similar to
+# cat(1). If $probe_type is present and non-NULL, returns success if $file
+# exists. If $probe_type is equal to $PROBE_SIZE, prints the size of $file in
+# bytes to standard-out.
+#
+f_media_get_directory()
+{
+ local dev="$1" file="$2" probe_type="$3" path
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_directory: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ $dev get private path
+ f_media_generic_get "$path" "$file" "$probe_type"
+}
+
+# f_media_shutdown_directory $device
+#
+# Shuts down the Directory device. Return status should be ignored.
+#
+f_media_shutdown_directory()
+{
+ DIRECTORY_CHECKED=
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/directory.subr
+
+fi # ! $_MEDIA_DIRECTORY_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/dos.subr b/usr.sbin/bsdconfig/share/media/dos.subr
new file mode 100644
index 0000000..df91aeb
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/dos.subr
@@ -0,0 +1,165 @@
+if [ ! "$_MEDIA_DOS_SUBR" ]; then _MEDIA_DOS_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/dos.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+DOS_MOUNTED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_dos
+#
+# Return success if we both found and set the media type to be a DOS partition.
+#
+f_media_set_dos()
+{
+ f_media_close
+
+ local devs ndevs
+ f_device_find "" $DEVICE_TYPE_DOS devs
+ f_count ndevs $devs
+
+ if [ ${ndevs:=0} -eq 0 ]; then
+ f_show_msg "$msg_no_dos_primary_partitions_found"
+ return $FAILURE
+ elif [ $ndevs -eq 1 ]; then
+ f_struct_copy $devs device_media
+ else
+ local dev
+ local title="$msg_choose_a_dos_partition"
+ local prompt="$msg_please_select_dos_partition"
+ local hline=
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_DOS \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $FAILURE
+
+ f_struct_copy "$dev" device_media
+ fi
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_dos $device
+#
+# Initializes the DOS media device. Returns success if able to mount the DOS
+# partition device using mount_msdosfs(8).
+#
+f_media_init_dos()
+{
+ local funcname=f_media_init_dos
+ local dev="$1" devname err
+
+ $dev get devname devname || return $FAILURE
+ f_dprintf "Init routine called for DOS device. devname=[%s]" \
+ "$devname"
+
+ if [ "$DOS_MOUNTED" ]; then
+ f_dprintf "DOS device already mounted."
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$MOUNTPOINT" ]; then
+ f_eval_catch $funcname mkdir 'mkdir -p "%s"' "$MOUNTPOINT" ||
+ return $FAILURE
+ fi
+
+ if ! f_eval_catch -dk err $funcname mount_msdosfs \
+ 'mount_msdosfs "%s" "%s"' "$devname" "$MOUNTPOINT"
+ then
+ err="${err#mount_msdosfs: }"; err="${err#$devname: }"
+ f_show_msg "$msg_error_mounting_device" \
+ "$devname" "$MOUNTPOINT" "$err"
+ return $FAILURE
+ fi
+ DOS_MOUNTED=1
+ return $SUCCESS
+}
+
+# f_media_get_dos $device $file [$probe_type]
+#
+# Returns data from $file on a mounted DOS partition device. Similar to cat(1).
+# If $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_get_dos()
+{
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_dos: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_type"
+}
+
+# f_media_shutdown_dos $device
+#
+# Shuts down the DOS partition device using umount(8). Return status should be
+# ignored.
+#
+f_media_shutdown_dos()
+{
+ local funcname=f_media_shutdown_dos
+ local dev="$1" err
+
+ [ "$DOS_MOUNTED" ] || return $FAILURE
+
+ if ! f_eval_catch -dk err $funcname umount \
+ 'umount -f "%s"' "$MOUNTPOINT"
+ then
+ err="${err#umount: }"; err="${err#*: }"
+ f_show_msg "$msg_could_not_unmount_the_dos_partition" \
+ "$MOUNTPOINT" "$err"
+ else
+ DOS_MOUNTED=
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/dos.subr
+
+fi # ! $_MEDIA_DOS_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/floppy.subr b/usr.sbin/bsdconfig/share/media/floppy.subr
new file mode 100644
index 0000000..bf402e5
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/floppy.subr
@@ -0,0 +1,229 @@
+if [ ! "$_MEDIA_FLOPPY_SUBR" ]; then _MEDIA_FLOPPY_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/floppy.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+FLOPPY_MOUNTED=
+FLOPPY_DISTWANTED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_floppy
+#
+# Return success if we both found and set the media type to be a floppy.
+#
+f_media_set_floppy()
+{
+ f_media_close
+
+ local devs ndevs
+ f_device_find "" $DEVICE_TYPE_FLOPPY devs
+ f_count ndevs $devs
+
+ if [ ${ndevs:=0} -eq 0 ]; then
+ f_interactive && f_show_msg "$msg_no_floppy_devices_found"
+ return $FAILURE
+ elif [ $ndevs -eq 1 ]; then
+ f_struct_copy $devs device_media
+ else
+ local dev
+ local title="$msg_choose_a_floppy_drive"
+ local prompt="$msg_please_select_a_floppy_drive"
+ local hline=
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_FLOPPY \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $FAILURE
+
+ f_struct_copy "$dev" device_media
+ fi
+
+ f_struct device_media &&
+ device_media unset private
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_floppy $device
+#
+# Initializes the Floppy media device. Returns success if able to mount the
+# Floppy disk device using either mount_msdosfs(8) or mount(8) (tried in that
+# order).
+#
+f_media_init_floppy()
+{
+ local funcname=f_media_init_floppy
+ local dev="$1" devname err
+
+ $dev get devname devname || return $FAILURE
+ f_dprintf "Init floppy called for %s distribution. devname=[%s]" \
+ "${FLOPPY_DISTWANTED:-some}" "$devname"
+
+ if [ "$FLOPPY_MOUNTED" ]; then
+ f_dprintf "Floppy device already mounted."
+ return $SUCCESS
+ fi
+
+ local mp
+ $dev get private mp
+ if [ ! -e "${mp:=$MOUNTPOINT}" ] && ! f_quietly mkdir -p "$mp"; then
+ f_show_msg "$msg_unable_to_make_directory_mountpoint" \
+ "$mp" "$devname"
+ return $FAILURE
+ fi
+
+ if f_interactive; then
+ local desc
+ $dev get desc desc
+ if [ "$FLOPPY_DISTWANTED" ]; then
+ f_show_msg "$msg_please_insert_floppy_in_drive" "$desc"
+ else
+ f_show_msg "$msg_please_insert_floppy_containing" \
+ "$FLOPPY_DISTWANTED" "$desc"
+ fi
+ fi
+
+ if ! {
+ f_eval_catch -dk err $funcname mount_msdosfs \
+ 'mount_msdosfs -o ro -m 0777 -u 0 -g 0 "%s" "%s"' \
+ "$devname" "$mp" ||
+ f_eval_catch -dk err $funcname mount \
+ 'mount -o ro "%s" "%s"' "$devname" "$mp"
+ }; then
+ err="${err#mount: }"; err="${err#*: }"
+ local name
+ $dev get name name
+ f_show_msg "$msg_error_mounting_floppy_device" \
+ "$name" "$devname" "$mp" "$err"
+ return $FAILURE
+ fi
+ FLOPPY_MOUNTED=1
+ FLOPPY_DISTWANTED=
+ return $SUCCESS
+}
+
+# f_media_get_floppy $device $file [$probe_type]
+#
+# Returns data from $file on a mounted Floppy disk device. Similar to cat(1).
+# If $probe_type is present and non-NULL, limits retries to zero and returns
+# success if $file exists. If $probe_type is equal to $PROBE_SIZE, prints the
+# size of $file in bytes to standard-out.
+#
+f_media_get_floppy()
+{
+ local funcname=f_media_get_floppy
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_floppy: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ #
+ # floppies don't use f_media_generic_get() because it's too expensive
+ # to speculatively open files on a floppy disk. Make user get it
+ # right or give up with floppies.
+ #
+ local mp
+ $dev get private mp
+ local fp="${mp:=$MOUNTPOINT}/$file"
+ if ! [ -f "$fp" -a -r "$fp" ]; then
+ local nretries=4
+ [ "$probe_type" = "$PROBE_SIZE" ] && echo "-1"
+ [ "$probe_type" ] && return $FAILURE
+ while ! [ -f "$fp" -a -r "$fp" ]; do
+ if [ $nretries -eq 0 ]; then
+ f_show_msg "$msg_failed_to_get_floppy_file" \
+ "$fp"
+ [ "$probe_type" = "$PROBE_SIZE" ] && echo "-1"
+ return $FAILURE
+ fi
+ FLOPPY_DISTWANTED="$fp"
+ f_media_shutdown_floppy "$dev"
+ f_media_init_floppy "$dev" || return $FAILURE
+ nretries=$(( $nretries - 1 ))
+ done
+ fi
+ #
+ # If we reach here, $file exists
+ #
+ if [ "$probe_type" = "$PROBE_SIZE" ]; then
+ local size
+ f_eval_catch -dk size $funcname stat 'stat -f %%z "%s"' "$fp"
+ f_isinteger "$size" || size=-1
+ echo "$size"
+ fi
+ [ "$probe_type" ] && return $SUCCESS
+ cat "$fp"
+}
+
+# f_media_shutdown_floppy $device
+#
+# Shuts down the Floppy disk device using umount(8). Return status should be
+# ignored.
+#
+f_media_shutdown_floppy()
+{
+ local funcname=f_media_shutdown_floppy
+ local dev="$1" err mp
+
+ [ "$FLOPPY_MOUNTED" ] || return $FAILURE
+
+ $dev get private mp
+ if f_eval_catch -d $funcname umount \
+ 'umount -f "%s"' "${mp:=$MOUNTPOINT}"
+ then
+ FLOPPY_MOUNTED=
+ if f_interactive && [ "$_systemState" != "fixit" ]; then
+ local desc
+ $dev get desc desc
+ f_show_msg "$msg_you_may_remove_the_floppy" "$desc"
+ fi
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/floppy.subr
+
+fi # ! $_MEDIA_FLOPPY_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/ftp.subr b/usr.sbin/bsdconfig/share/media/ftp.subr
new file mode 100644
index 0000000..a249a01
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/ftp.subr
@@ -0,0 +1,911 @@
+if [ ! "$_MEDIA_FTP_SUBR" ]; then _MEDIA_FTP_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/ftp.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+FTP_SKIP_RESOLV=
+
+URL_MAX=261261 # according to actual fetch(1) test-results
+
+FTP_DIRS="
+ .
+ releases/$UNAME_P
+ snapshots/$UNAME_P
+ pub/FreeBSD
+ pub/FreeBSD/releases/$UNAME_P
+ pub/FreeBSD/snapshots/$UNAME_P
+ pub/FreeBSD-Archive/old-releases/$UNAME_P
+" # END-QUOTE
+
+############################################################ FUNCTIONS
+
+# f_dialog_menu_media_ftp
+#
+# Prompt the user to select from a range of ``built-in'' FTP servers or specify
+# their own. If the user makes a choice and doesn't cancel or press Esc, stores
+# the user's choice in VAR_FTP_PATH (see variables.subr) and returns success.
+#
+f_dialog_menu_media_ftp()
+{
+ f_dialog_title "$msg_please_select_a_freebsd_ftp_distribution_site"
+ local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
+ f_dialog_title_restore
+ local prompt="$msg_please_select_the_site_closest_to_you_or_other"
+ local menu_list="
+ '$msg_main_site' 'ftp.freebsd.org'
+ 'URL' '$msg_specify_some_other_ftp_site'
+ 'IPv6 $msg_main_site' 'ftp.freebsd.org'
+ ' IPv6 $msg_france' 'ftp4.fr.freebsd.org'
+ ' IPv6 $msg_france #8' 'ftp8.fr.freebsd.org'
+ ' IPv6 $msg_ireland' 'ftp3.ie.freebsd.org'
+ ' IPv6 $msg_japan' 'ftp2.jp.freebsd.org'
+ ' IPv6 $msg_sweden' 'ftp4.se.freebsd.org'
+ ' IPv6 $msg_usa' 'ftp4.us.freebsd.org'
+ ' IPv6 $msg_turkey' 'ftp2.tr.freebsd.org'
+ '$msg_primary' 'ftp1.freebsd.org'
+ ' $msg_primary #2' 'ftp2.freebsd.org'
+ ' $msg_primary #3' 'ftp3.freebsd.org'
+ ' $msg_primary #4' 'ftp4.freebsd.org'
+ ' $msg_primary #5' 'ftp5.freebsd.org'
+ ' $msg_primary #6' 'ftp6.freebsd.org'
+ ' $msg_primary #7' 'ftp7.freebsd.org'
+ ' $msg_primary #10' 'ftp10.freebsd.org'
+ ' $msg_primary #11' 'ftp11.freebsd.org'
+ ' $msg_primary #12' 'ftp12.freebsd.org'
+ ' $msg_primary #13' 'ftp13.freebsd.org'
+ ' $msg_primary #14' 'ftp14.freebsd.org'
+ '$msg_armenia' 'ftp1.am.freebsd.org'
+ '$msg_australia' 'ftp.au.freebsd.org'
+ ' $msg_australia #2' 'ftp2.au.freebsd.org'
+ ' $msg_australia #3' 'ftp3.au.freebsd.org'
+ '$msg_austria' 'ftp.at.freebsd.org'
+ '$msg_brazil' 'ftp2.br.freebsd.org'
+ ' $msg_brazil #3' 'ftp3.br.freebsd.org'
+ ' $msg_brazil #4' 'ftp4.br.freebsd.org'
+ '$msg_canada' 'ftp.ca.freebsd.org'
+ '$msg_china' 'ftp.cn.freebsd.org'
+ '$msg_czech_republic' 'ftp.cz.freebsd.org'
+ '$msg_denmark' 'ftp.dk.freebsd.org'
+ '$msg_estonia' 'ftp.ee.freebsd.org'
+ '$msg_finland' 'ftp.fi.freebsd.org'
+ '$msg_france' 'ftp.fr.freebsd.org'
+ ' $msg_france #3' 'ftp3.fr.freebsd.org'
+ ' $msg_france #4' 'ftp4.fr.freebsd.org'
+ ' $msg_france #5' 'ftp5.fr.freebsd.org'
+ ' $msg_france #6' 'ftp6.fr.freebsd.org'
+ ' $msg_france #7' 'ftp7.fr.freebsd.org'
+ ' $msg_france #8' 'ftp8.fr.freebsd.org'
+ '$msg_germany' 'ftp.de.freebsd.org'
+ ' $msg_germany #2' 'ftp2.de.freebsd.org'
+ ' $msg_germany #4' 'ftp4.de.freebsd.org'
+ ' $msg_germany #5' 'ftp5.de.freebsd.org'
+ ' $msg_germany #6' 'ftp6.de.freebsd.org'
+ ' $msg_germany #7' 'ftp7.de.freebsd.org'
+ ' $msg_germany #8' 'ftp8.de.freebsd.org'
+ '$msg_greece' 'ftp.gr.freebsd.org'
+ ' $msg_greece #2' 'ftp2.gr.freebsd.org'
+ '$msg_ireland' 'ftp3.ie.freebsd.org'
+ '$msg_israel' 'ftp.il.freebsd.org'
+ '$msg_italy' 'ftp.it.freebsd.org'
+ '$msg_japan' 'ftp.jp.freebsd.org'
+ ' $msg_japan #2' 'ftp2.jp.freebsd.org'
+ ' $msg_japan #3' 'ftp3.jp.freebsd.org'
+ ' $msg_japan #4' 'ftp4.jp.freebsd.org'
+ ' $msg_japan #5' 'ftp5.jp.freebsd.org'
+ ' $msg_japan #6' 'ftp6.jp.freebsd.org'
+ ' $msg_japan #7' 'ftp7.jp.freebsd.org'
+ ' $msg_japan #8' 'ftp8.jp.freebsd.org'
+ ' $msg_japan #9' 'ftp9.jp.freebsd.org'
+ '$msg_korea' 'ftp.kr.freebsd.org'
+ ' $msg_korea #2' 'ftp2.kr.freebsd.org'
+ '$msg_latvia' 'ftp.lv.freebsd.org'
+ '$msg_lithuania' 'ftp.lt.freebsd.org'
+ '$msg_netherlands' 'ftp.nl.freebsd.org'
+ ' $msg_netherlands #2' 'ftp2.nl.freebsd.org'
+ '$msg_new_zealand' 'ftp.nz.freebsd.org'
+ '$msg_norway' 'ftp.no.freebsd.org'
+ '$msg_poland' 'ftp.pl.freebsd.org'
+ ' $msg_poland #2' 'ftp2.pl.freebsd.org'
+ '$msg_russia' 'ftp.ru.freebsd.org'
+ ' $msg_russia #2' 'ftp2.ru.freebsd.org'
+ ' $msg_russia #4' 'ftp4.ru.freebsd.org'
+ ' $msg_russia #5' 'ftp5.ru.freebsd.org'
+ ' $msg_russia #6' 'ftp6.ru.freebsd.org'
+ '$msg_slovak_republic' 'ftp.sk.freebsd.org'
+ ' $msg_slovak_republic #2' 'ftp2.sk.freebsd.org'
+ '$msg_slovenia' 'ftp.si.freebsd.org'
+ '$msg_south_africa' 'ftp.za.freebsd.org'
+ ' $msg_south_africa #2' 'ftp2.za.freebsd.org'
+ ' $msg_south_africa #4' 'ftp4.za.freebsd.org'
+ '$msg_spain' 'ftp.es.freebsd.org'
+ ' $msg_spain #3' 'ftp3.es.freebsd.org'
+ '$msg_sweden' 'ftp.se.freebsd.org'
+ ' $msg_sweden #2' 'ftp2.se.freebsd.org'
+ ' $msg_sweden #3' 'ftp3.se.freebsd.org'
+ ' $msg_sweden #4' 'ftp4.se.freebsd.org'
+ ' $msg_sweden #6' 'ftp6.se.freebsd.org'
+ '$msg_switzerland' 'ftp.ch.freebsd.org'
+ '$msg_taiwan' 'ftp.tw.freebsd.org'
+ ' $msg_taiwan #2' 'ftp2.tw.freebsd.org'
+ ' $msg_taiwan #3' 'ftp3.tw.freebsd.org'
+ ' $msg_taiwan #4' 'ftp4.tw.freebsd.org'
+ ' $msg_taiwan #6' 'ftp6.tw.freebsd.org'
+ ' $msg_taiwan #11' 'ftp11.tw.freebsd.org'
+ '$msg_uk' 'ftp.uk.freebsd.org'
+ ' $msg_uk #2' 'ftp2.uk.freebsd.org'
+ ' $msg_uk #3' 'ftp3.uk.freebsd.org'
+ ' $msg_uk #4' 'ftp4.uk.freebsd.org'
+ ' $msg_uk #5' 'ftp5.uk.freebsd.org'
+ '$msg_ukraine' 'ftp.ua.freebsd.org'
+ ' $msg_ukraine #7' 'ftp7.ua.freebsd.org'
+ '$msg_usa #1' 'ftp1.us.freebsd.org'
+ ' $msg_usa #2' 'ftp2.us.freebsd.org'
+ ' $msg_usa #3' 'ftp3.us.freebsd.org'
+ ' $msg_usa #4' 'ftp4.us.freebsd.org'
+ ' $msg_usa #5' 'ftp5.us.freebsd.org'
+ ' $msg_usa #6' 'ftp6.us.freebsd.org'
+ ' $msg_usa #8' 'ftp8.us.freebsd.org'
+ ' $msg_usa #10' 'ftp10.us.freebsd.org'
+ ' $msg_usa #11' 'ftp11.us.freebsd.org'
+ ' $msg_usa #13' 'ftp13.us.freebsd.org'
+ ' $msg_usa #14' 'ftp14.us.freebsd.org'
+ ' $msg_usa #15' 'ftp15.us.freebsd.org'
+ " # END-QUOTE
+ local hline="$msg_select_a_site_thats_close"
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$title\" \
+ \"\$btitle\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local mtag
+ mtag=$( eval $DIALOG \
+ --title \"\$title\" \
+ --backtitle \"\$btitle\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ ) || return $DIALOG_CANCEL
+ f_dialog_data_sanitize mtag
+
+ case "$mtag" in
+ URL) setvar $VAR_FTP_PATH "other" ;;
+ *)
+ local value
+ value=$( eval f_dialog_menutag2item \"\$mtag\" $menu_list )
+ setvar $VAR_FTP_PATH "ftp://$value"
+ esac
+
+ return $DIALOG_OK
+}
+
+# f_media_set_ftp
+#
+# Return success if we both found and set the media type to be an FTP server.
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_FTP_PATH
+# Can be a URL (including "ftp://" protocol-prefix) or "other"
+# (user is prompted to enter FTP URL). If a URL, can optionally
+# contain directory prefix after hostname/port. Valid examples
+# include:
+# ftp://myhost
+# ftp://somename:21/pub/
+# ftp://192.168.2.3/pub/
+# ftp://[::1]:21/
+# The default port if not specified is 21.
+# VAR_NAMESERVER [Optional]
+# If set, overrides resolv.conf(5) and sets the nameserver that
+# is used to convert names into addresses (when a name converts
+# into multiple addresses, the first address to successfully
+# connect is used).
+#
+# Meanwhile, the following variables from variable.subr are set after
+# successful execution:
+#
+# VAR_FTP_HOST
+# The FTP host to connect to, parsed from VAR_FTP_PATH. In the
+# example case of IPv6 where VAR_FTP_PATH is "ftp://[::1]", this
+# variable will be set to "::1" (the outer brackets are removed).
+# VAR_FTP_PORT
+# The TCP port to connect to, parsed from VAR_FTP_PATH. Usually
+# 21 unless VAR_FTP_PATH was of one of the following forms:
+# ftp://hostname:OTHER_PORT
+# ftp://hostname:OTHER_PORT/*
+# ftp://ip:OTHER_PORT
+# ftp://ip:OTHER_PORT/*
+# ftp://[ip6]:OTHER_PORT
+# ftp://[ip6]:OTHER_PORT/*
+# VAR_FTP_DIR
+# If VAR_FTP_PATH contained a directory element (e.g.,
+# "ftp://localhost/pub") this variable contains only the
+# directory element (e.g., "/pub").
+#
+f_media_set_ftp()
+{
+ f_media_close
+
+ local url
+ f_getvar $VAR_FTP_PATH url
+
+ # If we've been through here before ...
+ if f_struct device_network && [ "${url#$msg_other}" ]; then
+ f_dialog_yesno "$msg_reuse_old_ftp_site_selection_values" ||
+ url=
+ fi
+
+ if [ ! "$url" ]; then
+ f_dialog_menu_media_ftp || return $FAILURE
+ f_getvar $VAR_FTP_PATH url
+ fi
+ [ "$url" ] || return $FAILURE
+
+ case "$url" in
+ other)
+ setvar $VAR_FTP_PATH "ftp://"
+ f_variable_get_value $VAR_FTP_PATH \
+ "$msg_please_specify_url_of_a_freebsd_distribution"
+ f_getvar $VAR_FTP_PATH url
+ if [ ! "${url#ftp://}" ]; then
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ fi
+ if [ ${#url} -gt ${URL_MAX:-261261} ]; then
+ f_show_msg "$msg_length_of_specified_url_is_too_long" \
+ ${#url} ${URL_MAX:-261261}
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ fi
+ case "$url" in
+ ftp://*) : valid URL ;;
+ *)
+ f_show_msg "$msg_sorry_invalid_url" "$url"
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ esac
+ esac
+ case "$url" in
+ ftp://*) : valid URL ;;
+ *)
+ f_show_msg "$msg_sorry_invalid_url" "$url"
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ esac
+
+ # Set the name of the FTP device to the URL
+ f_struct_new DEVICE device_ftp
+ device_ftp set name "$url"
+
+ if ! f_struct device_network ||
+ ! f_dialog_yesno "$msg_youve_already_done_the_network_configuration"
+ then
+ f_struct device_network &&
+ f_device_shutdown device_network
+ if ! f_device_select_tcp; then
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ fi
+ local dev if
+ f_getvar $VAR_NETWORK_DEVICE if
+ f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
+ f_struct_copy "$dev" device_network
+ fi
+ if ! f_device_init device_network; then
+ f_dprintf "f_media_set_ftp: %s" "$msg_net_device_init_failed"
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ fi
+
+ local hostname="${url#*://}" port=21 dir=/
+ case "$hostname" in
+ #
+ # The order in-which the below individual cases appear is important!
+ #
+ "["*"]":*/*) # IPv6 address with port and directory
+ f_dprintf "Looks like an IPv6 addr with port/dir: %s" \
+ "$hostname"
+ hostname="${hostname#\[}"
+ port="${hostname#*\]:}"
+ port="${port%%[!0-9]*}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%\]:*}"
+ ;;
+ "["*"]":*) # IPv6 address with port
+ f_dprintf "Looks like an IPv6 addr with port: %s" "$hostname"
+ hostname="${hostname#\[}"
+ port="${hostname#*\]:}"
+ port="${port%%[!0-9]*}"
+ hostname="${hostname%%\]:*}"
+ ;;
+ "["*"]"/*) # IPv6 address with directory
+ f_dprintf "Looks like an IPv6 addr with dir: %s" "$hostname"
+ hostname="${hostname#\[}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%\]*}"
+ ;;
+ "["*"]") # IPv6 address
+ f_dprintf "Looks like an IPv6 addr: %s" "$hostname"
+ hostname="${hostname#\[}"
+ hostname="${hostname%\]}"
+ ;;
+ #
+ # ^^^ IPv6 above / DNS Name or IPv4 below vvv
+ #
+ *:*/*) # DNS name or IPv4 address with port and directory
+ f_dprintf "Looks like a %s with port/dir: %s" \
+ "DNS name or IPv4 addr" "$hostname"
+ port="${hostname#*:}"
+ port="${port%%[!0-9]*}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%:*}"
+ ;;
+ *:*) # DNS name or IPv4 address with port
+ f_dprintf "Looks like a DNS name or IPv4 addr with port: %s" \
+ "$hostname"
+ port="${hostname#*:}"
+ hostname="${hostname%%:*}"
+ ;;
+ */*) # DNS name or IPv4 address with directory
+ f_dprintf "Looks like a DNS name or IPv4 addr with dir: %s" \
+ "$hostname"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%/*}"
+ ;;
+ *) # DNS name or IPv4 address
+ f_dprintf "Looks like a DNS name or IPv4 addr: %s" "$hostname"
+ : leave hostname as-is
+ esac
+
+ f_dprintf "hostname = \`%s'" "$hostname"
+ f_dprintf "dir = \`%s'" "$dir"
+ f_dprintf "port \# = \`%d'" "$port"
+
+ local ns
+ f_getvar $VAR_NAMESERVER ns
+ [ "$ns" ] || f_resolv_conf_nameservers ns
+ if [ "$ns" -a ! "$FTP_SKIP_RESOLV" ] && ! {
+ f_validate_ipaddr "$hostname" ||
+ f_validate_ipaddr6 "$hostname"
+ }; then
+ f_show_info "$msg_looking_up_host" "$hostname"
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_media_set_ftp" "$hostname"
+ if ! f_quietly f_host_lookup "$hostname"; then
+ f_show_msg "$msg_cannot_resolve_hostname" "$hostname"
+ f_struct device_network &&
+ f_device_shutdown device_network
+ f_struct_free device_network
+ unset $VAR_FTP_PATH
+ return $FAILURE
+ fi
+ f_dprintf "Found DNS entry for %s successfully." "$hostname"
+ fi
+
+ setvar $VAR_FTP_HOST "$hostname"
+ setvar $VAR_FTP_PORT "$port"
+ setvar $VAR_FTP_DIR "$dir"
+
+ device_ftp set type $DEVICE_TYPE_FTP
+ device_ftp set init f_media_init_ftp
+ device_ftp set get f_media_get_ftp
+ device_ftp set shutdown f_media_shutdown_ftp
+ device_ftp set private device_network
+ f_struct_copy device_ftp device_media
+ f_struct_free device_ftp
+
+ return $SUCCESS
+}
+
+# f_media_set_ftp_active
+#
+# Wrapper to f_media_set_ftp to access FTP servers actively.
+#
+f_media_set_ftp_active()
+{
+ setvar $VAR_FTP_STATE "active"
+ f_media_set_ftp
+}
+
+# f_media_set_ftp_passive
+#
+# Wrapper to f_media_set_ftp to access FTP servers passively.
+#
+f_media_set_ftp_passive()
+{
+ setvar $VAR_FTP_STATE "passive"
+ f_media_set_ftp
+}
+
+# f_media_set_ftp_userpass
+#
+# Prompt the user to enter/confirm the username/password variables that will
+# be used to communicate with the FTP servers. Returns success if the user does
+# not cancel or press Esc to either username or password.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_FTP_USER
+# The username to send via ftp(1) when connecting to an FTP
+# server.
+# VAR_FTP_PASS
+# The password to send with the above username.
+#
+# Does not prompt for confirmation of values if VAR_NONINTERACTIVE is set (see
+# variable.subr for more information).
+#
+f_media_set_ftp_userpass()
+{
+ local user pass
+ f_variable_get_value $VAR_FTP_USER \
+ "$msg_please_enter_the_username_you_wish_to_login_as"
+ f_getvar $VAR_FTP_USER user
+ if [ "$user" ]; then
+ f_variable_get_value $VAR_FTP_PASS \
+ "$msg_please_enter_the_password_for_this_user"
+ f_getvar $VAR_FTP_PASS pass
+ else
+ pass=
+ fi
+ [ "$pass" ] # Return status
+}
+
+# f_device_network_up $device
+#
+# Brings up attached network device, if any - takes FTP device as arg.
+#
+f_device_network_up()
+{
+ local dev="$1" netDev
+ f_struct "$dev" || return $FAILURE
+ $dev get private netDev || return $SUCCESS # No net == happy net
+debug=1 f_dprintf "netDev=[$netDev]"
+ f_device_init $netDev
+}
+
+# f_device_network_down $device
+#
+# Brings down attached network device, if any - takes FTP device as arg.
+#
+f_device_network_down()
+{
+ local dev="$1" netDev
+ f_struct "$dev" || return $FAILURE
+ $dev get private netDev || return $SUCCESS
+ f_device_shutdown $netDev
+}
+
+# f_media_init_ftp $device
+#
+# Initializes the FTP media device. Returns success if both able to log into
+# the FTP server and confirm the existence of at least one known release path
+# using ftp(1).
+#
+# Variables from variable.subr used to initialize the connection are as follows
+# (all of which are configured by f_media_set_ftp above):
+#
+# VAR_FTP_PATH
+# The unparsed FTP URL representing the server to contact.
+# Usually "ftp://server" for example. Can contain TCP port number
+# and/or directory path (but should not contain username/password
+# info).
+# VAR_FTP_HOST
+# The FTP host to connect to. Can be an IPv4 address (e.g.,
+# 127.0.0.1), IPv6 address (e.g., ::1), or DNS hostname. Usually
+# set automatically in f_media_set_ftp() by parsing VAR_FTP_PATH.
+# VAR_FTP_PORT
+# The TCP port to connect to. Usually set automatically in
+# f_media_set_ftp() by parsing VAR_FTP_PATH.
+# VAR_FTP_DIR
+# The base FTP directory to use when downloading files from the
+# FTP server. Usually set automatically in f_media_set_ftp() by
+# parsing VAR_FTP_PATH.
+# VAR_FTP_USER [Optional]
+# If unset, defaults to using anonymous access.
+# VAR_FTP_PASS [Optional]
+# If unset, defaults to a sensible value.
+#
+# In addition, the following (managed either manually or by f_media_set_ftp_*):
+#
+# VAR_FTP_STATE
+# Sets FTPMODE for ftp(1) and can be one of:
+# active active mode FTP only
+# auto automatic determination of passive or active
+# (this is the default)
+# gate gate-ftp mode
+# passive passive mode FTP only
+# See ftp(1) for additional information.
+#
+# And last, but not least (managed automatically or manually):
+#
+# VAR_RELNAME
+# Defaults to being set to $(uname -r) but can be overridden.
+# This sets the name of a release to look for as part of a well
+# known set of paths to search for release data once connected
+# via FTP. If set to "__RELEASE" or "any" then the VAR_FTP_DIR is
+# taken as the absolute path to the release and no further
+# searching is done (see FTP_DIRS above in the GLOBALS section
+# for a list of well known paths that are used when searching for
+# a VAR_RELNAME sub-directory).
+#
+f_media_init_ftp()
+{
+ local dev="$1"
+ local url
+
+ $dev get name url
+ f_dprintf "Init routine called for FTP device. url=[%s]" "$url"
+
+ if [ "$FTP_INITIALIZED" ]; then
+ f_dprintf "FTP device already initialized."
+ return $SUCCESS
+ fi
+
+ # If we can't initialize the network, bag it!
+ f_device_network_up $dev || return $FAILURE
+
+ local cp
+ while :; do
+ f_getvar $VAR_FTP_PATH cp
+ if [ ! "$cp" ]; then
+ if ! f_media_set_ftp ||
+ ! f_getvar $VAR_FTP_PATH cp ||
+ [ ! "$cp" ]
+ then
+ f_show_msg "$msg_unable_to_get_proper_ftp_path"
+ f_device_network_down $dev
+ return $FAILURE
+ fi
+ fi
+
+ local ftp_host ftp_dir
+ if ! {
+ f_getvar $VAR_FTP_HOST ftp_host &&
+ f_getvar $VAR_FTP_DIR ftp_dir
+ }; then
+ f_show_msg "$msg_missing_ftp_host_or_directory"
+ f_device_network_down $dev
+ return $FAILURE
+ fi
+
+ local ftp_port
+ f_getvar $VAR_FTP_PORT ftp_port
+ local host="$ftp_host" port="${ftp_port:+:$ftp_port}"
+ case "$host" in *:*) host="[$host]"; esac
+
+ local user pass use_anon=
+ f_getvar $VAR_FTP_USER user
+ if [ ! "$user" ]; then
+ user="anonymous"
+ use_anon=1
+ fi
+ if ! f_getvar $VAR_FTP_PASS pass; then
+ f_getvar $VAR_HOSTNAME cp
+ if f_running_as_init; then
+ pass="installer@$cp"
+ else
+ local name="$( id -un 2> /dev/null )"
+ pass="${name:-ftp}@$cp"
+ fi
+ fi
+
+ f_show_info "$msg_logging_in_to_user_at_host" \
+ "$user" "$ftp_host"
+
+ local userpass=""
+ if [ ! "$use_anon" ] && [ "$user" -o "$pass" ]; then
+ userpass="$user${pass:+:$( f_uriencode "$pass" )}"
+ userpass="$userpass${userpass:+@}"
+ fi
+
+ local mode rx
+ f_getvar $VAR_FTP_STATE mode
+
+ if [ "$ftp_dir" ]; then
+ if ! rx=$(
+ printf 'cd "%s"\npwd\n' "$ftp_dir" | eval \
+ FTPMODE=\"\$mode\" \
+ ${use_anon:+FTPANONPASS=\"\$pass\"} \
+ ftp -V ${use_anon:+-a} \
+ \"ftp://\$userpass\$host\$port\" \
+ 2>&1
+ ); then
+ f_show_msg "$msg_couldnt_open_ftp_connection" \
+ "$ftp_host" "$rx"
+ break # to failure
+ fi
+ if echo "$rx" | awk -v dir="/${ftp_dir#/}" '
+ BEGIN {
+ found = 0
+ if ( dir != "/" ) sub("/$", "", dir)
+ }
+ /^Remote directory: / {
+ sub(/^[^:]*:[[:space:]]*/, "")
+ if ($0 != dir) next
+ found = 1; exit
+ }
+ END { exit ! found }
+ '; then
+ setvar $VAR_FTP_DIR "$ftp_dir"
+ setvar $VAR_FTP_PATH \
+ "ftp://$ftp_host/${ftp_dir#/}"
+ else
+ f_show_msg \
+ "$msg_please_check_the_url_and_try_again" \
+ "ftp://$ftp_host/${ftp_dir#/}"
+ break # to failure
+ fi
+ fi
+
+ #
+ # Now that we've verified that the path we're given is ok,
+ # let's try to be a bit intelligent in locating the release we
+ # are looking for. First off, if the release is specified as
+ # "__RELEASE" or "any", then just assume that the current
+ # directory is the one we want and give up.
+ #
+ local rel
+ f_getvar $VAR_RELNAME rel
+ f_dprintf "f_media_init_ftp: rel=[%s]" "$rel"
+
+ case "$rel" in
+ __RELEASE|any)
+ FTP_INITIALIZED=YES
+ return $SUCCESS
+ ;;
+ *)
+ #
+ # Ok, since we have a release variable, let's walk
+ # through the list of directories looking for a release
+ # directory. First successful CWD wins.
+ #
+ if ! rx=$(
+ for dir in $FTP_DIRS; do
+ # Avoid confusing some servers
+ [ "$dir" = "." ] && continue
+ printf 'cd "/%s/%s"\npwd\n' \
+ "$dir" "$rel"
+ done | eval \
+ FTPMODE=\"\$mode\" \
+ ${use_anon:+FTPANONPASS=\"\$pass\"} \
+ ftp -V ${use_anon:+-a} \
+ \"ftp://\$userpass\$host\$port\" \
+ 2>&1
+ ); then
+ f_show_msg "$msg_couldnt_open_ftp_connection" \
+ "$ftp_host" "$rx"
+ break # to failure
+ fi
+
+ local fdir
+ if fdir=$( echo "$rx" | awk '
+ /^Remote directory: / {
+ sub(/^[^:]*:[[:space:]]*/, "")
+ if ($0 == "/") next
+ # Exit after the first dir
+ found++; print; exit
+ }
+ END { exit ! found }
+ ' ); then
+ setvar $VAR_FTP_DIR "$fdir"
+ setvar $VAR_FTP_PATH "ftp://$ftp_host$fdir"
+ FTP_INITIALIZED=YES
+ return $SUCCESS
+ else
+ f_yesno "$msg_cant_find_distribution" \
+ "$rel" "$ftp_host"
+ if [ $? -eq $DIALOG_OK ]; then
+ unset $VAR_FTP_PATH
+ f_media_set_ftp && continue
+ fi
+ fi
+ esac
+ break # to failure
+ done
+
+ unset FTP_INITIALIZED $VAR_FTP_PATH
+ f_device_network_down $dev
+ return $FAILURE
+}
+
+# f_media_get_ftp $device $file [$probe_type]
+#
+# Returns data from $file on an FTP server using ftp(1). Please note that
+# $device is unused but must be present (even if null). Information is instead
+# gathered from the environment. If $probe_type is present and non-NULL,
+# returns success if $file exists. If $probe_type is equal to $PROBE_SIZE,
+# prints the size of $file in bytes to standard-out.
+#
+# Variables from variable.subr used to configure the connection are as follows
+# (all of which are configured by f_media_set_ftp above):
+#
+# VAR_FTP_HOST
+# FTP host to connect to. Can be an IPv4 address, IPv6 address,
+# or DNS hostname of your choice.
+# VAR_FTP_PORT
+# TCP port to connect on; see f_media_set_ftp() above.
+# VAR_FTP_USER [Optional]
+# If unset, defaults to using anonymous access.
+# VAR_FTP_PASS [Optional]
+# If unset, defaults to a sensible value.
+#
+# In addition, the following (managed either manually or by f_media_set_ftp_*):
+#
+# VAR_FTP_STATE
+# Sets FTPMODE for ftp(1) and can be one of:
+# active active mode FTP only
+# auto automatic determination of passive or active
+# (this is the default)
+# gate gate-ftp mode
+# passive passive mode FTP only
+# See ftp(1) for additional information.
+#
+# See variable.subr for additional information.
+#
+# Example usage:
+# f_media_set_ftp
+# f_media_get_ftp media $file
+#
+f_media_get_ftp()
+{
+ local funcname=f_media_get_ftp
+ local dev="$1" file="$2" probe_type="$3" hosts=
+
+ f_dprintf "f_media_get_ftp: dev=[%s] file=[%s] probe_type=%s" \
+ "$dev" "$file" "$probe_type"
+
+ local ftp_host ftp_port
+ f_getvar $VAR_FTP_HOST ftp_host
+ f_getvar $VAR_FTP_PORT ftp_port
+
+ if [ ! "$FTP_INITIALIZED" ]; then
+ f_dprintf "No FTP connection open, can't get file %s" "$file"
+ return $FAILURE
+ fi
+
+ if ! {
+ f_validate_ipaddr "$ftp_host" ||
+ f_validate_ipaddr6 "$ftp_host" ||
+ {
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_media_get_ftp" "$ftp_host"
+ f_host_lookup "$ftp_host" hosts
+ }
+ }; then
+ # All the above validations failed
+ [ "$hosts" ] && f_dialog_msgbox "$hosts"
+ return $FAILURE
+ elif [ ! "$hosts" ]; then
+ # One of the first two validations passed
+ hosts="$ftp_host"
+ fi
+
+ local host connected=
+ for host in $hosts; do
+ f_quietly nc -nz "$host" "$ftp_port" || continue
+ connected=1; break
+ done
+ if [ ! "$connected" ]; then
+ f_show_msg "$msg_couldnt_connect_to_ftp_server %s:%s" \
+ "$ftp_host" "$ftp_port"
+ return $FAILURE
+ fi
+
+ local user pass use_anon=
+ f_getvar $VAR_FTP_USER user
+ if [ ! "$user" ]; then
+ user="anonymous"
+ use_anon=1
+ fi
+ if ! f_getvar $VAR_FTP_PASS pass; then
+ f_getvar $VAR_HOSTNAME cp
+ if f_running_as_init; then
+ pass="installer@$cp"
+ else
+ local name="$( id -un 2> /dev/null )"
+ pass="${name:-ftp}@$cp"
+ fi
+ fi
+
+ local userpass=""
+ if [ ! "$use_anon" ] && [ "$user" -o "$pass" ]; then
+ userpass="$user${pass:+:$( f_uriencode "$pass" )}"
+ userpass="$userpass${userpass:+@}"
+ fi
+
+ local dir mode rx
+ f_getvar $VAR_FTP_DIR\#/ dir
+ f_getvar $VAR_FTP_STATE mode
+
+ local port="${ftp_port:+:$ftp_port}"
+ case "$host" in *:*) host="[$host]"; esac
+
+ f_dprintf "sending ftp request for: %s" "ftp://$host$port/$dir/$file"
+
+ if [ "$probe_type" ]; then
+ local url="ftp://$userpass$host$port/$dir/$file" size
+ [ "$use_anon" ] && url="ftp://$host$port/$dir/$file"
+ if ! f_eval_catch -dk size $funcname fetch \
+ 'fetch -s "%s"' "$url" || ! f_isinteger "$size"
+ then
+ f_dprintf "size request failed!"
+ [ "$probe_type" = "$PROBE_SIZE" ] && echo "-1"
+ return $FAILURE
+ fi
+ [ "$probe_type" = "$PROBE_SIZE" ] && echo "$size"
+ return $SUCCESS
+ fi
+
+ eval FTPMODE=\"\$mode\" ${use_anon:+FTPANONPASS=\"\$pass\"} \
+ ftp -V ${use_anon:+-a} -o - \
+ \"ftp://\$userpass\$host\$port/\$dir/\$file\" 2> /dev/null
+ local retval=$?
+
+ [ $retval -eq $SUCCESS ] || f_dprintf "request failed!"
+ return $retval
+}
+
+# f_media_shutdown_ftp $device
+#
+# Shuts down the FTP device. Return status should be ignored. Note that since
+# we don't maintain an open connection to the FTP server there's nothing to do.
+#
+f_media_shutdown_ftp()
+{
+ [ "$FTP_INITIALIZED" ] || return $SUCCESS
+
+ unset FTP_INITIALIZED
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/ftp.subr
+
+fi # ! $_MEDIA_FTP_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/http.subr b/usr.sbin/bsdconfig/share/media/http.subr
new file mode 100644
index 0000000..b928f7d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/http.subr
@@ -0,0 +1,688 @@
+if [ ! "$_MEDIA_HTTP_SUBR" ]; then _MEDIA_HTTP_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/http.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+HTTP_SKIP_RESOLV=
+
+URL_MAX=261261
+ # NOTE: This is according to actual fetch(1) test-results. We actually
+ # use nc(1) to retrieve files, but it's still a good idea to keep the
+ # URLs short enough that fetch(1) won't complain.
+
+HTTP_DIRS="
+ .
+ releases/$UNAME_P
+ snapshots/$UNAME_P
+ pub/FreeBSD
+ pub/FreeBSD/releases/$UNAME_P
+ pub/FreeBSD/snapshots/$UNAME_P
+ pub/FreeBSD-Archive/old-releases/$UNAME_P
+" # END-QUOTE
+
+############################################################ FUNCTIONS
+
+# f_dialog_menu_media_http
+#
+# Prompt the user to select from a range of ``built-in'' HTTP servers or
+# specify their own. If the user makes a choice and doesn't cancel or press
+# Esc, stores the user's choice in VAR_FTP_PATH (see variable.subr) and returns
+# success.
+#
+f_dialog_menu_media_http()
+{
+ f_dialog_title "$msg_please_select_a_freebsd_http_distribution_site"
+ local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
+ f_dialog_title_restore
+ local prompt="$msg_please_select_the_site_closest_to_you_or_other"
+ local menu_list="
+ 'dist $msg_main_site' 'ftp.freebsd.org'
+ 'pkg $msg_main_site' 'pkg.freebsd.org'
+ 'URL' '$msg_specify_some_other_http_site'
+ " # END-QUOTE
+ local hline="$msg_select_a_site_thats_close"
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$title\" \
+ \"\$btitle\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local mtag
+ mtag=$( eval $DIALOG \
+ --title \"\$title\" \
+ --backtitle \"\$btitle\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ ) || return $DIALOG_CANCEL
+ f_dialog_data_sanitize mtag
+
+ case "$mtag" in
+ URL) setvar $VAR_HTTP_PATH "other" ;;
+ *)
+ local value
+ value=$( eval f_dialog_menutag2item \"\$mtag\" $menu_list )
+ setvar $VAR_HTTP_PATH "http://$value"
+ esac
+
+ return $DIALOG_OK
+}
+
+# f_media_set_http
+#
+# Return success if we both found and set the media type to be an HTTP server.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_HTTP_PATH
+# URL containing host and optionally a target path to the release
+# repository on the HTTP server. Valid examples include:
+# http://myhost
+# http://somename:80/pub/
+# http://192.168.2.3/pub/
+# http://[::1]:8000/
+# The default port if not specified is 80.
+# VAR_NAMESERVER [Optional]
+# If set, overrides resolv.conf(5) and sets the nameserver that
+# is used to convert names into addresses (when a name converts
+# into multiple addresses, the first address to successfully
+# connect is used).
+#
+# Meanwhile, the following variables from variable.subr are set after
+# successful execution:
+#
+# VAR_HTTP_HOST
+# The HTTP host to connect to, parsed from VAR_HTTP_PATH. In the
+# example case of IPv6 where VAR_HTTP_PATH is "http://[::1]" this
+# variable will be set to "::1" (the outer brackets are removed).
+# VAR_HTTP_PORT
+# The TCP port to connect to, parsed from VAR_HTTP_PATH. Usually
+# 80 unless VAR_HTTP_PATH was one of the following forms:
+# http://hostname:OTHER_PORT
+# http://hostname:OTHER_PORT/*
+# http://ip:OTHER_PORT
+# http://ip:OTHER_PORT/*
+# http://[ip6]:OTHER_PORT
+# http://[ip6]:OTHER_PORT/*
+# VAR_HTTP_DIR
+# If VAR_HTTP_PATH contained a directory element (e.g.,
+# "http://localhost/pub") this variable contains only the
+# directory element (e.g., "/pub").
+#
+f_media_set_http()
+{
+ f_media_close
+
+ local url
+ f_getvar $VAR_HTTP_PATH url
+
+ # If we've been through here before ...
+ if f_struct device_network && [ "${url#$msg_other}" ]; then
+ f_dialog_yesno "$msg_reuse_old_http_site_settings" || url=
+ fi
+
+ if [ ! "$url" ]; then
+ f_dialog_menu_media_http || return $FAILURE
+ f_getvar $VAR_HTTP_PATH url
+ fi
+ [ "$url" ] || return $FAILURE
+
+ case "$url" in
+ other)
+ setvar $VAR_HTTP_PATH "http://"
+ f_variable_get_value $VAR_HTTP_PATH \
+ "$msg_please_specify_url_of_freebsd_http_distribution"
+ f_getvar $VAR_HTTP_PATH url
+ if [ ! "${url#http://}" ]; then
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ fi
+ if [ ${#url} -gt ${URL_MAX:-261261} ]; then
+ f_show_msg "$msg_length_of_specified_url_is_too_long" \
+ ${#url} ${URL_MAX:-261261}
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ fi
+ case "$url" in
+ http://*) : valid URL ;;
+ *)
+ f_show_msg "$msg_sorry_invalid_url" "$url"
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ esac
+ esac
+ case "$url" in
+ http://*) : valid URL ;;
+ *)
+ f_show_msg "$msg_sorry_invalid_url" "$url"
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ esac
+
+ # Set the name of the HTTP device to the URL
+ f_struct_new DEVICE device_http
+ device_http set name "$url"
+
+ if ! f_struct device_network ||
+ ! f_dialog_yesno "$msg_youve_already_done_the_network_configuration"
+ then
+ f_struct device_network &&
+ f_device_shutdown device_network
+ if ! f_device_select_tcp; then
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ fi
+ local dev if
+ f_getvar $VAR_NETWORK_DEVICE if
+ f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
+ f_struct_copy "$dev" device_network
+ fi
+ if ! f_device_init device_network; then
+ f_dprintf "f_media_set_http: %s" "$msg_net_device_init_failed"
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ fi
+
+ local hostname="${url#*://}" port=80 dir=/
+ case "$hostname" in
+ #
+ # The order in-which the below individual cases appear is important!
+ #
+ "["*"]":*/*) # IPv6 address with port and directory
+ f_dprintf "Looks like an IPv6 addr with port/dir: %s" \
+ "$hostname"
+ hostname="${hostname#\[}"
+ port="${hostname#*\]:}"
+ port="${port%%[!0-9]*}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%\]:*}"
+ ;;
+ "["*"]":*) # IPv6 address with port
+ f_dprintf "Looks like an IPv6 addr with port: %s" "$hostname"
+ hostname="${hostname#\[}"
+ port="${hostname#*\]:}"
+ port="${port%%[!0-9]*}"
+ hostname="${hostname%%\]:*}"
+ ;;
+ "["*"]"/*) # IPv6 address with directory
+ f_dprintf "Looks like an IPv6 addr with dir: %s" "$hostname"
+ hostname="${hostname#\[}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%\]*}"
+ ;;
+ "["*"]") # IPv6 address
+ f_dprintf "Looks like an IPv6 addr: %s" "$hostname"
+ hostname="${hostname#\[}"
+ hostname="${hostname%\]}"
+ ;;
+ #
+ # ^^^ IPv6 above / DNS Name or IPv4 below vvv
+ #
+ *:*/*) # DNS name or IPv4 address with port and directory
+ f_dprintf "Looks like a %s with port/dir: %s" \
+ "DNS name or IPv4 addr" "$hostname"
+ port="${hostname#*:}"
+ port="${port%%[!0-9]*}"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%:*}"
+ ;;
+ *:*) # DNS name or IPv4 address with port
+ f_dprintf "Looks like a DNS name or IPv4 addr with port: %s" \
+ "$hostname"
+ port="${hostname#*:}"
+ hostname="${hostname%%:*}"
+ ;;
+ */*) # DNS name or IPv4 address with directory
+ f_dprintf "Looks like a DNS name or IPv4 addr with dir: %s" \
+ "$hostname"
+ dir="/${hostname#*/}"
+ hostname="${hostname%%/*}"
+ ;;
+ *) # DNS name or IPv4 address
+ f_dprintf "Looks like a DNS name or IPv4 addr: %s" "$hostname"
+ : leave hostname as-is
+ esac
+
+ f_dprintf "hostname = \`%s'" "$hostname"
+ f_dprintf "dir = \`%s'" "$dir"
+ f_dprintf "port \# = \`%d'" "$port"
+
+ local ns
+ f_getvar $VAR_NAMESERVER ns
+ [ "$ns" ] || f_resolv_conf_nameservers ns
+ if [ "$ns" -a ! "$HTTP_SKIP_RESOLV" ] && ! {
+ f_validate_ipaddr "$hostname" ||
+ f_validate_ipaddr6 "$hostname"
+ }; then
+ f_show_info "$msg_looking_up_host" "$hostname"
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_media_set_http" "$hostname"
+ if ! f_quietly f_host_lookup "$hostname"; then
+ f_show_msg "$msg_cannot_resolve_hostname" "$hostname"
+ f_struct device_network &&
+ f_device_shutdown device_network
+ f_struct_free device_network
+ unset $VAR_HTTP_PATH
+ return $FAILURE
+ fi
+ f_dprintf "Found DNS entry for %s successfully." "$hostname"
+ fi
+
+ setvar $VAR_HTTP_HOST "$hostname"
+ setvar $VAR_HTTP_PORT "$port"
+ setvar $VAR_HTTP_DIR "$dir"
+
+ device_http set type $DEVICE_TYPE_HTTP
+ device_http set init f_media_init_http
+ device_http set get f_media_get_http
+ device_http set shutdown f_media_shutdown_http
+ device_http set private device_network
+ f_struct_copy device_http device_media
+ f_struct_free device_http
+
+ return $SUCCESS
+}
+
+# f_http_check_access [$connect_only]
+#
+# Return success if able list a remote HTTP directory. If $connect_only is
+# present and non-null, then returns success if a connection can be made.
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_HTTP_HOST
+# The HTTP server host name, IPv4 address or IPv6 address.
+# Valid examples include:
+# myhost
+# 192.168.2.3
+# ::1
+# VAR_HTTP_PORT
+# The TCP port to connect to when communicating with the server.
+# VAR_HTTP_PATH
+# The HTTP path sent to the server. Unused if $connect_only is
+# present and non-NULL.
+#
+f_http_check_access()
+{
+ local connect_only="$1" hosts=
+
+ local http_host http_port
+ f_getvar $VAR_HTTP_HOST http_host
+ f_getvar $VAR_HTTP_PORT http_port
+
+ if ! {
+ f_validate_ipaddr "$http_host" ||
+ f_validate_ipaddr6 "$http_host" ||
+ {
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_http_check_access" "$http_host"
+ f_host_lookup "$http_host" hosts
+ }
+ }; then
+ # All the above validations failed
+ [ "$hosts" ] && f_dialog_msgbox "$hosts"
+ unset $VAR_HTTP_HOST
+ return $FAILURE
+ elif [ ! "$hosts" ]; then
+ # One of the first two validations passed
+ hosts="$http_host"
+ fi
+
+ local host connected=
+ for host in $hosts; do
+ f_quietly nc -nz "$host" "$http_port" || continue
+ connected=1; break
+ done
+ if [ ! "$connected" ]; then
+ f_show_msg "$msg_couldnt_connect_to_server http://%s:%s/" \
+ "$http_host" "$http_port"
+ unset $VAR_HTTP_HOST
+ return $FAILURE
+ fi
+ [ "$connect_only" ] && return $SUCCESS
+
+ local http_path
+ f_getvar $VAR_HTTP_PATH http_path
+ f_show_info "$msg_checking_access_to" "$http_path"
+
+ local rx
+ case "$http_path" in
+ http://*|/*) : valid request ;;
+ *) http_path="/$http_path" # full URI requests only
+ esac
+ if ! rx=$(
+ printf "GET %s/ HTTP/1.0\r\n\r\n" "${http_path%/}" |
+ nc -n "$host" "$http_port"
+ ); then
+ f_show_msg "$msg_couldnt_connect_to_server http://%s:%s/" \
+ "$http_host" "$http_port"
+ unset $VAR_HTTP_HOST
+ return $FAILURE
+ fi
+
+ local hdr
+ hdr=$( echo "$rx" | awk '/^\r$/{exit}{print}' )
+
+ local http_found=$FAILURE
+ if echo "$hdr" | awk '
+ BEGIN { found = 0 }
+ /^HTTP.... 200 / {
+ found = 1
+ exit
+ }
+ END { exit ! found }
+ '; then
+ http_found=$SUCCESS
+ fi
+
+ return $http_found
+}
+
+# f_media_init_http $device
+#
+# Initializes the HTTP media device. Returns success if able to confirm the
+# existence of at least one known HTTP server release path directly via HTTP
+# using f_http_check_access(), above.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_HTTP_HOST
+# The HTTP server to connect to. Must be set. Also see
+# f_http_check_access() for additional variables.
+# VAR_RELNAME
+# Usually set to `uname -r' but can be overridden.
+# VAR_HTTP_PATH
+# The HTTP path sent to the server. Usually set by calling
+# f_media_set_http().
+#
+# Meanwhile, after successful execution, the following variables (also from
+# variable.subr) are set:
+#
+# VAR_HTTP_PATH
+# The [possibly] adjusted VAR_HTTP_PATH that was found to contain
+# a valid FreeBSD repository.
+#
+f_media_init_http()
+{
+ local dev="$1"
+ f_dprintf "Init routine called for HTTP device. dev=[%s]" "$dev"
+
+ if [ "$HTTP_INITIALIZED" ]; then
+ f_dprintf "HTTP device already initialized."
+ return $SUCCESS
+ fi
+
+ #
+ # First verify access
+ #
+ local connect_only=1
+ f_http_check_access $connect_only
+
+ local http_host
+ f_getvar $VAR_HTTP_HOST http_host
+ while [ ! "$http_host" ]; do
+ f_media_set_http || return $FAILURE
+ f_http_check_access $connect_only
+ f_getvar $VAR_HTTP_HOST http_host
+ done
+
+ local http_path http_found=$FAILURE
+ while :; do
+ #
+ # Now that we've verified that the path we're given is ok,
+ # let's try to be a bit intelligent in locating the release we
+ # are looking for. First off, if the release is specified as
+ # "__RELEASE" or "any", then just assume that the current
+ # directory is the one we want and give up.
+ #
+ local rel
+ f_getvar $VAR_RELNAME rel
+ f_dprintf "f_media_init_http: rel=[%s]" "$rel"
+
+ case "$rel" in
+ __RELEASE|any)
+ f_getvar $VAR_HTTP_DIR $VAR_HTTP_PATH
+ f_http_check_access
+ http_found=$?
+ ;;
+ *)
+ #
+ # Ok, since we have a release variable, let's walk
+ # through the list of directories looking for a release
+ # directory. First successful path wins.
+ #
+ local fdir hp
+ f_getvar $VAR_HTTP_PATH%/ hp
+ setvar $VAR_HTTP_PATH "$hp/$PKG_ABI/latest"
+ if [ "$PKG_ABI" ] && f_http_check_access; then
+ http_found=$SUCCESS
+ setvar $VAR_HTTP_PATH "$hp"
+ else
+ for fdir in $HTTP_DIRS; do
+ setvar $VAR_HTTP_PATH "$hp/$fdir/$rel"
+ if f_http_check_access; then
+ http_found=$SUCCESS
+ break
+ fi
+ done
+ fi
+ esac
+
+ [ $http_found -eq $SUCCESS ] && HTTP_INITIALIZED=YES break
+
+ f_getvar $VAR_HTTP_PATH http_path
+ f_show_msg "$msg_please_check_the_url_and_try_again" \
+ "$http_path"
+
+ unset HTTP_INITIALIZED $VAR_HTTP_PATH
+ f_media_set_http || break
+ done
+
+ return $http_found
+}
+
+# f_media_get_http $device $file [$probe_type]
+#
+# Returns data from $file on an HTTP server using nc(1). Please note that
+# $device is unused but must be present (even if null). Information is instead
+# gathered from the environment. If $probe_type is both present and non-NULL,
+# this function exits after receiving the HTTP header response from the server
+# (if the HTTP response code is 200, success is returned; otherwise failure).
+# If $probe_type is equal to $PROBE_SIZE, prints the content-length in bytes
+# from the response (or -1 if not found) to standard-out.
+#
+# The variables used to configure the connection are as follows (all of which
+# are configured by f_media_set_http above):
+#
+# VAR_HTTP_HOST
+# HTTP server which to connect. Can be an IPv4 address, IPv6
+# address, or DNS hostname of your choice.
+# VAR_HTTP_PORT
+# TCP port to connect on; see f_media_set_http above.
+# VAR_HTTP_PATH
+# Directory prefix to use when requesting $file. Default is `/'
+# unless f_media_init_http was able to use f_http_check_access
+# to validate one of the defaults in $HTTP_DIRS (see GLOBALS at
+# the top of this file); assuming VAR_RELNAME was not set to
+# either `__RELEASE' or `any' (indicating that the global set of
+# $HTTP_DIRS should be ignored).
+#
+# See variable.subr for additional information.
+#
+# Example usage:
+# f_media_set_http
+# f_media_get_http media $file
+#
+f_media_get_http()
+{
+ local dev="$1" file="$2" probe_type="$3" hosts=
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_http: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ local http_host http_port
+ f_getvar $VAR_HTTP_HOST http_host
+ f_getvar $VAR_HTTP_PORT http_port
+
+ if [ ! "$HTTP_INITIALIZED" ]; then
+ f_dprintf "No HTTP connection open, can't get file %s" "$file"
+ return $FAILURE
+ fi
+
+ if ! {
+ f_validate_ipaddr "$http_host" ||
+ f_validate_ipaddr6 "$http_host" ||
+ {
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_media_get_http" "$http_host"
+ f_host_lookup "$http_host" hosts
+ }
+ }; then
+ # All the above validations failed
+ [ "$hosts" ] && f_dialog_msgbox "$hosts"
+ return $FAILURE
+ elif [ ! "$hosts" ]; then
+ # One of the first two validations passed
+ hosts="$http_host"
+ fi
+
+ local host connected=
+ for host in $hosts; do
+ f_quietly nc -nz "$host" "$http_port" || continue
+ connected=1; break
+ done
+ if [ ! "$connected" ]; then
+ f_show_msg "$msg_couldnt_connect_to_server http://%s:%s/" \
+ "$http_host" "$http_port"
+ return $FAILURE
+ fi
+
+ local http_path
+ f_getvar $VAR_HTTP_PATH%/ http_path
+ case "$http_path" in
+ http://*|/*) : valid request ;;
+ *) http_path="/$http_path" # full URI requests only
+ esac
+
+ local url="$http_path/$file" rx
+ f_dprintf "sending http request for: %s" "$url"
+ f_dprintf "using nc to connect to: %s:%s" "$host" "$http_port"
+ printf "GET %s HTTP/1.0\r\n\r\n" "$url" | nc -n "$host" "$http_port" |
+ (
+ #
+ # scan the headers of the response
+ # this is extremely quick'n dirty
+ #
+
+ rv=0 length=-1
+ while read LINE; do
+ case "$LINE" in
+ HTTP*)
+ f_dprintf "received response: %s" "$LINE"
+ set -- $LINE; rv=$2
+ f_isinteger "$rv" || rv=0
+ ;;
+ "Content-Length: "*)
+ length="${LINE% }"
+ length="${length#Content-Length: }"
+ f_dprintf "received content-length: %s" \
+ "$length"
+ ;;
+ *)
+ [ "${LINE% }" ] || break # End of headers
+ esac
+ done
+
+ [ $rv -ge 500 ] && exit 5
+ [ $rv -eq 404 ] && exit 44
+ [ $rv -ge 400 ] && exit 4
+ [ $rv -ge 300 ] && exit 3
+ [ $rv -eq 200 ] || exit $FAILURE
+
+ if [ ! "$probe_type" ]; then
+ cat # output the rest ``as-is''
+ elif [ "$probe_type" = "$PROBE_SIZE" ]; then
+ f_isinteger "$length" || length=-1
+ echo "$length"
+ fi
+ exit 200
+ )
+ local retval=$?
+ [ $retval -eq 200 ] && return $SUCCESS
+ [ "$probe_type" ] && return $FAILURE
+
+ case "$retval" in
+ 5) f_show_msg "$msg_server_error_when_requesting_url" "$url" ;;
+ 44) f_show_msg "$msg_url_was_not_found" "$url" ;;
+ 4) f_show_msg "$msg_client_error" ;;
+ *) f_show_msg "$msg_error_when_requesting_url" "$url" ;;
+ esac 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ return $FAILURE
+}
+
+# f_media_shutdown_http $device
+#
+# Shuts down the HTTP device. Return status should be ignored. Note that since
+# we don't maintain an open connection to the HTTP server, nothing to do.
+#
+f_media_shutdown_http()
+{
+ [ "$HTTP_INITIALIZED" ] || return $SUCCESS
+
+ unset HTTP_INITIALIZED
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/http.subr
+
+fi # ! $_MEDIA_HTTP_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/httpproxy.subr b/usr.sbin/bsdconfig/share/media/httpproxy.subr
new file mode 100644
index 0000000..1ef516f
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/httpproxy.subr
@@ -0,0 +1,463 @@
+if [ ! "$_MEDIA_HTTPPROXY_SUBR" ]; then _MEDIA_HTTPPROXY_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/httpproxy.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/ftp.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ FUNCTIONS
+
+# f_media_set_http_proxy
+#
+# Return success if we both found and set the media type to be an ftp server,
+# accessed via http proxy.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_HTTP_PROXY
+# HTTP Proxy server to use. Valid examples include:
+# myhost
+# somename:3128
+# 192.168.2.3
+# [::1]:8080
+# The default port if not specified is 3128.
+#
+# Variables from variable.subr that are set after successful execution include
+# the following:
+#
+# VAR_HTTP_PROXY_HOST The host portion of VAR_HTTP_PROXY.
+# VAR_HTTP_PROXY_PORT The TCP port parsed from VAR_HTTP_PROXY.
+#
+# See also f_media_set_ftp() for additional variables.
+#
+f_media_set_http_proxy()
+{
+ FTP_SKIP_RESOLV=1 f_media_set_ftp || return $FAILURE
+
+ f_variable_get_value $VAR_HTTP_PROXY \
+ "$msg_please_enter_the_address_of_the_http_proxy"
+
+ local proxy
+ f_getvar $VAR_HTTP_PROXY proxy
+ [ "$proxy" ] || return $FAILURE
+
+ local hostname="$proxy" port=3128
+ case "$hostname" in
+ #
+ # The order in-which the below individual cases appear is important!
+ #
+ "["*"]":*) # IPv6 address with port
+ f_dprintf "Looks like an IPv6 addr with port: %s" "$hostname"
+ hostname="${hostname#\[}"
+ port="${hostname#*\]:}"
+ port="${port%%[!0-9]*}"
+ hostname="${hostname%%\]:*}"
+ ;;
+ "["*"]") # IPv6 address
+ f_dprintf "Looks like an IPv6 addr: %s" "$hostname"
+ hostname="${hostname#\[}"
+ hostname="${hostname%\]}"
+ ;;
+ #
+ # ^^^ IPv6 above / DNS Name or IPv4 below vvv
+ #
+ *:*) # DNS name or IPv4 address with port
+ f_dprintf "Looks like a DNS name or IPv4 addr with port: %s" \
+ "$hostname"
+ port="${hostname#*:}"
+ hostname="${hostname%%:*}"
+ ;;
+ *) # DNS name or IPv4 address
+ f_dprintf "Looks like a DNS name or IPv4 addr: %s" "$hostname"
+ : leave hostname as-is
+ esac
+
+ setvar $VAR_HTTP_PROXY_HOST "$hostname"
+ setvar $VAR_HTTP_PROXY_PORT "$port"
+
+ if f_debugging; then
+ f_dprintf "VAR_FTP_PATH : %s" "$( f_getvar $VAR_FTP_PATH )"
+ f_dprintf "VAR_HTTP_PROXY_HOST, _PORT: %s:%s" \
+ "$( f_getvar $VAR_HTTP_PROXY_HOST )" \
+ "$( f_getvar $VAR_HTTP_PROXY_PORT )"
+ fi
+
+ # media device has been set by f_media_set_ftp(), overwrite partly:
+ device_media set type $DEVICE_TYPE_HTTP_PROXY
+ device_media set init f_media_init_http_proxy
+ device_media set get f_media_get_http_proxy
+ device_media unset shutdown
+
+ return $SUCCESS
+}
+
+# f_http_proxy_check_access [$connect_only]
+#
+# Return success if able list a remote FTP directory via HTTP proxy. If
+# $connect_only is present and non-null, then returns success if a connection
+# can be made. Variables from variable.subr that can be used to script user
+# input:
+#
+# VAR_HTTP_PROXY_HOST
+# The HTTP proxy server host name, IPv4 address or IPv6 address.
+# Valid examples include:
+# myhost
+# 192.168.2.3
+# ::1
+# VAR_HTTP_PROXY_PORT
+# The TCP port to connect to when communicating with the HTTP
+# proxy server.
+# VAR_HTTP_PROXY_PATH
+# The FTP URL sent to the HTTP proxy server. Unused if
+# $connect_only is present and non-NULL.
+#
+f_http_proxy_check_access()
+{
+ local connect_only="$1" hosts=
+
+ local proxy_host proxy_port
+ f_getvar $VAR_HTTP_PROXY_HOST proxy_host
+ f_getvar $VAR_HTTP_PROXY_PORT proxy_port
+
+ if ! {
+ f_validate_ipaddr "$proxy_host" ||
+ f_validate_ipaddr6 "$proxy_host" ||
+ {
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_http_proxy_check_access" "$proxy_host"
+ f_host_lookup "$proxy_host" hosts
+ }
+ }; then
+ # All the above validations failed
+ [ "$hosts" ] && f_dialog_msgbox "$hosts"
+ unset $VAR_HTTP_PROXY_HOST
+ return $FAILURE
+ elif [ ! "$hosts" ]; then
+ # One of the first two validations passed
+ hosts="$proxy_host"
+ fi
+
+ local host connected=
+ for host in $hosts; do
+ f_quietly nc -nz "$host" "$proxy_port" || continue
+ connected=1; break
+ done
+ if [ ! "$connected" ]; then
+ f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
+ "$proxy_host" "$proxy_port"
+ unset $VAR_HTTP_PROXY_HOST
+ return $FAILURE
+ fi
+ [ "$connect_only" ] && return $SUCCESS
+
+ #
+ # Some proxies fetch files with certain extensions in "ascii mode"
+ # instead of "binary mode" for FTP. The FTP server then translates all
+ # LF to CRLF.
+ #
+ # You can force Squid to use binary mode by appending ";type=i" to the
+ # URL, which is what sysinstall(8) has traditionally done.
+ #
+
+ local proxy_path
+ f_getvar $VAR_HTTP_PROXY_PATH proxy_path
+ f_show_info "$msg_checking_access_to" "$proxy_path"
+
+ local rx
+ if ! rx=$(
+ printf "GET %s/ HTTP/1.0\r\n\r\n" "${proxy_path%/}" |
+ nc -n "$host" "$proxy_port"
+ ); then
+ f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
+ "$proxy_host" "$proxy_port"
+ unset $VAR_HTTP_PROXY_HOST
+ return $FAILURE
+ fi
+
+ local hdr
+ hdr=$( echo "$rx" | awk '/^\r$/{exit}{print}' )
+
+ local http_found=$FAILURE
+ if echo "$hdr" | awk '
+ BEGIN { found = 0 }
+ /^HTTP.... 200 / {
+ found = 1
+ exit
+ }
+ END { exit ! found }
+ '; then
+ http_found=$SUCCESS
+ fi
+
+ #
+ # Scan the headers of the response
+ # this is extremely quick'n dity
+ #
+
+ unset $VAR_HTTP_FTP_MODE
+ if echo "$hdr" | awk '
+ BEGIN { found = 0 }
+ {
+ if (!match($0, /^Server: /)) next
+ found = ( substr($0, 9, 5) ~ /[Ss]quid/ )
+ }
+ END { exit ! found }
+ '; then
+ setvar $VAR_HTTP_FTP_MODE ";type=i"
+ else
+ setvar $VAR_HTTP_FTP_MODE ""
+ fi
+
+ return $http_found
+}
+
+# f_media_init_http_proxy $device
+#
+# Initializes the HTTP Proxy media device. Returns success if able to confirm
+# the existence of at least one known FTP server release path via HTTP proxy
+# using f_http_proxy_check_access(), above.
+#
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_HTTP_PROXY_HOST
+# The HTTP proxy server to connect to. Usually set by having
+# f_media_set_http_proxy() parse VAR_HTTP_PROXY. Must be set.
+# Also see f_http_proxy_check_access() for additional variables.
+# VAR_RELNAME
+# Usually set to `uname -r' but can be overridden.
+# VAR_FTP_PATH
+# The FTP URL to send to the HTTP proxy server. Usually set by
+# calling f_media_set_ftp().
+#
+# Meanwhile, after successful execution, the following variables (also from
+# variable.subr) are set:
+#
+# VAR_HTTP_PROXY_PATH
+# The [possibly] adjusted VAR_FTP_PATH that was found to contain
+# a valid FreeBSD repository.
+#
+f_media_init_http_proxy()
+{
+ local dev="$1"
+ f_dprintf "Init routine called for HTTP Proxy device. dev=[%s]" "$dev"
+
+ #
+ # First verify access
+ #
+ local connect_only=1
+ f_http_proxy_check_access $connect_only
+
+ local proxy_host
+ f_getvar $VAR_HTTP_PROXY_HOST proxy_host
+ while [ ! "$proxy_host" ]; do
+ f_media_set_http_proxy || return $FAILURE
+ f_http_proxy_check_access $connect_only
+ f_getvar $VAR_HTTP_PROXY_HOST proxy_host
+ done
+
+ local rel proxy_path http_found=$FAILURE
+ while :; do
+ #
+ # If the release is specified as "__RELEASE" or "any", then
+ # just assume that the path the user gave is ok.
+ #
+ f_getvar $VAR_RELNAME rel
+ f_dprintf "f_media_init_http_proxy: rel=[%s]" "$rel"
+
+ case "$rel" in
+ __RELEASE|any)
+ f_getvar $VAR_FTP_PATH $VAR_HTTP_PROXY_PATH
+ f_http_proxy_check_access
+ http_found=$?
+ ;;
+ *)
+ local fdir fp
+ f_getvar $VAR_FTP_PATH%/ fp
+ for fdir in $FTP_DIRS; do
+ setvar $VAR_HTTP_PROXY_PATH "$fp/$fdir/$rel"
+ if f_http_proxy_check_access; then
+ http_found=$SUCCESS
+ break
+ fi
+ done
+ esac
+
+ [ $http_found -eq $SUCCESS ] && break
+
+ f_getvar $VAR_HTTP_PROXY_PATH proxy_path
+ f_show_msg "$msg_please_check_the_url_and_try_again" \
+ "$proxy_path"
+
+ unset $VAR_HTTP_PROXY_PATH
+ f_media_set_http_proxy || break
+ done
+
+ return $http_found
+}
+
+# f_media_get_http_proxy $device $file [$probe_type]
+#
+# Returns data from $file on an FTP server via HTTP proxy using nc(1). Please
+# note that $device is unused but must be present (even if null). Information
+# is instead gathered from the environment. If $probe_type is both present and
+# non-NULL, this function exits after receiving the HTTP header response from
+# the proxy server (if the HTTP response code is 200, success is returned;
+# otherwise failure). If $probe_type is equal to $PROBE_SIZE, prints the
+# content-length in bytes from the response (or -1 if not found) to standard-
+# out.
+#
+# The variables used to configure the connection are as follows (all of which
+# are configured by f_media_set_http_proxy above):
+#
+# VAR_HTTP_PROXY_HOST
+# HTTP proxy host to connect. Can be an IPv4 address, IPv6
+# address, or DNS hostname of your choice.
+# VAR_HTTP_PROXY_PORT
+# TCP port to connect on; see f_media_set_http_proxy above.
+# VAR_HTTP_PROXY_PATH
+# URL (including "ftp://" protocol-prefix) of FTP directory to
+# use as a prefix when requesting $file via HTTP proxy.
+#
+# See variable.subr for additional information.
+#
+# Example usage:
+# f_media_set_http_proxy
+# f_media_get_http_proxy media $file
+#
+f_media_get_http_proxy()
+{
+ local dev="$1" file="$2" probe_type="$3" hosts=
+
+ f_dprintf "f_media_get_http_proxy: dev=[%s] file=[%s] probe_type=%s" \
+ "$dev" "$file" "$probe_type"
+
+ local proxy_host proxy_port
+ f_getvar $VAR_HTTP_PROXY_HOST proxy_host
+ f_getvar $VAR_HTTP_PROXY_PORT proxy_port
+
+ if ! {
+ f_validate_ipaddr "$proxy_host" ||
+ f_validate_ipaddr6 "$proxy_host" ||
+ {
+ f_dprintf "%s: Looking up hostname, %s, using host(1)" \
+ "f_media_get_http_proxy" "$proxy_host"
+ f_host_lookup "$proxy_host" hosts
+ }
+ }; then
+ # All the above validations failed
+ [ "$hosts" ] && f_dialog_msgbox "$hosts"
+ return $FAILURE
+ elif [ ! "$hosts" ]; then
+ # One of the first two validations passed
+ hosts="$proxy_host"
+ fi
+
+ local host connected=
+ for host in $hosts; do
+ f_quietly nc -nz "$host" "$proxy_port" || continue
+ connected=1; break
+ done
+ if [ ! "$connected" ]; then
+ f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
+ "$proxy_host" "$proxy_port"
+ return $FAILURE
+ fi
+
+ local proxy_path mode
+ f_getvar $VAR_HTTP_PROXY_PATH%/ proxy_path
+ f_getvar $VAR_HTTP_FTP_MODE mode
+ local url="$proxy_path/$file$mode" rx
+
+ f_dprintf "sending http request for: %s" "$url"
+ printf "GET %s HTTP/1.0\r\n\r\n" "$url" | nc -n "$host" "$proxy_port" |
+ (
+ #
+ # scan the headers of the response
+ # this is extremely quick'n dirty
+ #
+
+ rv=0 length=-1
+ while read LINE; do
+ case "$LINE" in
+ HTTP*)
+ f_dprintf "received response: %s" "$LINE"
+ set -- $LINE; rv=$2
+ f_isinteger "$rv" || rv=0
+ ;;
+ "Content-Length: "*)
+ length="${LINE% }"
+ length="${length#Content-Length: }"
+ f_dprintf "received content-length: %s" \
+ "$length"
+ ;;
+ *)
+ [ "${LINE% }" ] || break # End of headers
+ esac
+ done
+
+ [ $rv -ge 500 ] && exit 5
+ [ $rv -eq 404 ] && exit 44
+ [ $rv -ge 400 ] && exit 4
+ [ $rv -ge 300 ] && exit 3
+ [ $rv -eq 200 ] || exit $FAILURE
+
+ if [ ! "$probe_type" ]; then
+ cat # output the rest ``as-is''
+ elif [ "$probe_type" = "$PROBE_SIZE" ]; then
+ f_isinteger "$length" || length=-1
+ echo "$length"
+ fi
+ exit 200
+ )
+ local retval=$?
+ [ $retval -eq 200 ] && return $SUCCESS
+ [ "$probe_type" ] && return $FAILURE
+
+ case "$retval" in
+ 5) f_show_msg "$msg_server_error_when_requesting_url" "$url" ;;
+ 44) f_show_msg "$msg_url_was_not_found" "$url" ;;
+ 4) f_show_msg "$msg_client_error" ;;
+ *) f_show_msg "$msg_error_when_requesting_url" "$url" ;;
+ esac 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ return $FAILURE
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/httpproxy.subr
+
+fi # ! $_MEDIA_HTTPPROXY_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/network.subr b/usr.sbin/bsdconfig/share/media/network.subr
new file mode 100644
index 0000000..6e17200
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/network.subr
@@ -0,0 +1,182 @@
+if [ ! "$_MEDIA_NETWORK_SUBR" ]; then _MEDIA_NETWORK_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/network.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+NETWORK_INITIALIZED=
+
+############################################################ FUNCTIONS
+
+# f_media_init_network $device
+#
+# Initialize a network device (such as `fxp0', `em0', etc.). Returns success if
+# able to successfully initialize the device. If not running as init (basically
+# from the FreeBSD install media) then assume that the network has already been
+# initialized and returns success.
+#
+# The variables (from variable.subr) used to initialize the network are as
+# follows (all of which are configured either automatically or manaully):
+#
+# VAR_IFCONFIG + device_name (e.g., `ifconfig_em0')
+# Automatically populated but can be overridden in a script. This
+# defines the ifconfig(8) properties specific to a chosen network
+# interface device. Optional if VAR_IPV6ADDR is set.
+# VAR_IPV6ADDR [Optional]
+# If not running as init (and setting up RTSOL connections for
+# the interface), then must be set manually. If set, used as the
+# IPv6 configuration for the given network interface device.
+# VAR_GATEWAY [Optional]
+# If not running as init (and setting up a static connection for
+# the interface) then must be set (usually via rc.conf(5), but
+# can be set manually to override). If unset, the user is warned
+# but not prevented from proceeding (as most connections need a
+# default route but not everyone).
+#
+f_media_init_network()
+{
+ local dev="$1"
+
+ f_dprintf "Init routine called for network device \`%s'." "$dev"
+ if [ "$NETWORK_INITIALIZED" ]; then
+ f_dprintf "Network already initialized."
+ return $SUCCESS
+ elif ! f_running_as_init; then
+ f_dprintf "Not running as init -- calling the deed done."
+ NETWORK_INITIALIZED=1
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$RESOLV_CONF" ]; then
+ if ! f_config_resolv; then
+ f_show_msg "$msg_cant_seem_to_write_out_resolv_conf" \
+ "$RESOLV_CONF"
+ return $FAILURE
+ fi
+ fi
+
+ local cp
+ if f_getvar $VAR_IFCONFIG$dev cp; then
+ #
+ # If this interface isn't a DHCP one, bring it up.
+ # If it is, then it's already up.
+ #
+ case "$cp" in
+ *DHCP*)
+ f_dprintf "A DHCP interface. Should already be up."
+ ;;
+ *)
+ f_dprintf "Not a DHCP interface."
+ if ! f_quietly ifconfig "$dev" $cp; then
+ f_show_msg "$msg_unable_to_configure_device" \
+ "$dev"
+ return $FAILURE
+ fi
+ local rp
+ f_getvar $VAR_GATEWAY rp
+ if [ ! "$rp" ]; then
+ f_show_msg "$msg_no_gateway_has_been_set"
+ else
+ #
+ # Explicitly flush all routes to get back to a
+ # known sane state. We don't need to check this
+ # exit code because if anything fails it will
+ # show up in the route add below.
+ #
+ f_quietly route -n flush
+ f_dprintf "Adding default route to %s." "$rp"
+ if ! f_quietly route -n add default "$rp"; then
+ f_show_msg \
+ "$msg_failed_to_add_default_route"
+ return $FAILURE
+ fi
+ fi
+ esac
+ elif ! { f_getvar $VAR_IPV6ADDR cp && [ "$cp" ]; }; then
+ f_show_msg "$msg_device_is_not_configured" "$dev"
+ return $FAILURE
+ fi
+
+ f_dprintf "Network initialized successfully."
+ NETWORK_INITIALIZED=1
+ return $SUCCESS
+}
+
+# f_media_shutdown_network $device
+#
+# Shuts down the configured network device (e.g., `fxp0', `em0', etc.) and
+# deletes the default route (if configured). Returns failure if the device
+# passed has not been configured. If not running as init (basically from the
+# FreeBSD install media) then does nothing and returns success.
+#
+f_media_shutdown_network()
+{
+ local dev="$1" cp
+
+ f_dprintf "Shutdown called for network device %s" "$dev"
+ if [ ! "$NETWORK_INITIALIZED" ]; then
+ f_dprintf "Network not initialized -- nothing to do."
+ return $SUCCESS
+ fi
+
+ unset NETWORK_INITIALIZED
+ unset $VAR_NETWORK_DEVICE
+
+ if ! f_running_as_init; then
+ f_dprintf "Not running as init -- calling the deed done."
+ return $SUCCESS
+ fi
+
+ f_getvar $VAR_IFCONFIG$dev cp || return $FAILURE
+ f_dprintf "ifconfig %s down" "$dev"
+ f_quietly ifconfig $dev down ||
+ f_show_msg "$msg_unable_to_down_the_interface_properly" "$dev"
+
+ if f_getvar $VAR_GATEWAY cp; then
+ f_dprintf "Deleting default route."
+ f_quietly route -n delete default
+ fi
+
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/network.subr
+
+fi # ! $_MEDIA_NETWORK_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/nfs.subr b/usr.sbin/bsdconfig/share/media/nfs.subr
new file mode 100644
index 0000000..d83661f
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/nfs.subr
@@ -0,0 +1,258 @@
+if [ ! "$_MEDIA_NFS_SUBR" ]; then _MEDIA_NFS_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/nfs.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/media/network.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+NFS_MOUNTED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_nfs
+#
+# Return success if we both found and set the media type to be an NFS server.
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_NFS_PATH
+# The NFS path specification (host:path) to use when mounting the
+# remote repository.
+# VAR_NAMESERVER [Optional]
+# Automatically populated from resolv.conf(5) but can be
+# overridden. If set, the host portion of VAR_NFS_PATH is
+# looked up using f_host_lookup() from `tcpip.subr'.
+#
+# Meanwhile, the following variables from variable.subr are set after
+# successful execution:
+#
+# VAR_NFS_HOST
+# The host portion of the NFS path specification, parsed from
+# VAR_NFS_PATH.
+#
+f_media_set_nfs()
+{
+ local nfs
+
+ f_media_close
+
+ f_variable_get_value $VAR_NFS_PATH \
+ "$msg_please_enter_the_full_nfs_file_specification"
+ f_getvar $VAR_NFS_PATH nfs
+ [ "$nfs" ] || return $FAILURE
+
+ case "$nfs" in
+ *:*) : valid NFS path ;;
+ *)
+ f_show_msg "$msg_invalid_nfs_path_specification"
+ return $FAILURE
+ esac
+
+ f_struct_new DEVICE device_nfs
+ device_nfs set name "$nfs"
+
+ if ! f_struct device_network ||
+ ! f_dialog_yesno "$msg_youve_already_done_the_network_configuration"
+ then
+ f_struct device_network &&
+ f_device_shutdown device_network
+ f_device_select_tcp || return $FAILURE
+ local dev if
+ f_getvar $VAR_NETWORK_DEVICE if
+ f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
+ f_struct_copy "$dev" device_network
+ fi
+ f_device_init device_network ||
+ f_dprintf "%s: $msg_net_device_init_failed\n" f_media_set_nfs
+
+ local hostname="${nfs%%:*}"
+ if f_isset $VAR_NAMESERVER && ! {
+ f_validate_ipaddr "$hostname" || f_validate_ipaddr6 "$hostname"
+ }; then
+ f_show_info "$msg_looking_up_host" "$hostname"
+ f_dprintf "%s Looking up hostname, %s, using host(1)" \
+ "f_media_set_nfs" "$hostname"
+ if ! f_quietly f_host_lookup "$hostname"; then
+ f_show_msg "$msg_cannot_resolve_hostname" "$hostname"
+ f_struct device_network &&
+ f_device_shutdown device_network
+ f_struct_free device_network
+ unset $VAR_NFS_PATH
+ return $FAILURE
+ fi
+ f_dprintf "Found DNS entry for %s successfully." "$hostname"
+ fi
+
+ setvar $VAR_NFS_HOST "$hostname"
+
+ device_nfs set type $DEVICE_TYPE_NFS
+ device_nfs set init f_media_init_nfs
+ device_nfs set get f_media_get_nfs
+ device_nfs set shutdown f_media_shutdown_nfs
+ device_nfs set private device_network # in name only (deref'd later)
+
+ f_struct_copy device_nfs device_media
+ f_struct_free device_nfs
+
+ return $SUCCESS
+}
+
+# f_media_init_nfs $device
+#
+# Initializes the NFS media device. Returns success if able to mount the NFS
+# device using mount_nfs(1).
+#
+# The variables (from variable.subr) used to initialize the NFS mount are as
+# follows (all of which are configured manually/optionally from the options
+# menu):
+#
+# VAR_NFS_TCP [Optional]
+# If non-NULL, adds the "tcp" option via `-o' to mount_nfs(8).
+# VAR_NFS_V3 [Optional]
+# If non-NULL, adds the "nfsv3" option via `-o' to mount_nfs(8).
+# VAR_NFS_SECURE [Optional]
+# If non-NULL, adds the "-P" flag to mount_nfs(8).
+# VAR_SLOW_ETHER [Optional]
+# If non-NULL, adjusts the read/write size to avoid timeouts.
+#
+f_media_init_nfs()
+{
+ local funcname=f_media_init_nfs
+ local dev="$1" name err
+
+ $dev get name name || return $FAILURE
+ f_dprintf "Init routine called for NFS device. name=[%s]" \
+ "$name"
+
+ if [ "$NFS_MOUNTED" ]; then
+ f_dprintf "NFS device already mounted."
+ return $SUCCESS
+ fi
+
+ if ! f_device_init device_network; then
+ f_dprintf "f_media_init_nfs: %s" "$msg_net_device_init_failed"
+ return $FAILURE
+ fi
+
+ if [ ! -e "$MOUNTPOINT" ]; then
+ f_eval_catch $funcname mkdir 'mkdir -p "%s"' "$MOUNTPOINT" ||
+ return $FAILURE
+ fi
+
+ local cp tcp="" use3="" secure="" readsize=4096 writesize=4096
+ f_getvar $VAR_NFS_TCP cp
+ [ "$cp" = "YES" ] && tcp=1
+ f_getvar $VAR_NFS_V3 cp
+ [ "$cp" = "YES" ] && use3=1
+ f_getvar $VAR_NFS_SECURE cp
+ [ "$cp" = "YES" ] && secure=1
+ f_getvar $VAR_SLOW_ETHER cp
+ [ "$cp" = "YES" ] && readsize=1024 writesize=1024
+
+ local options="rsize=$readsize,wsize=$writesize"
+ [ "$use3" ] && options="$options,nfsv3"
+ [ "$tcp" ] && options="$options,tcp"
+
+ if ! f_eval_catch -dk err $funcname mount_nfs \
+ 'mount_nfs %s -o "%s" "%s" "%s"' \
+ "${secure:+-P}" "$options" "$name" "$MOUNTPOINT"
+ then
+ err="${err#mount_nfs: }"
+ f_show_msg "$msg_error_mounting_device" \
+ "$name" "$MOUNTPOINT" "$err"
+ f_struct device_network &&
+ f_device_shutdown device_network
+ return $FAILURE
+ fi
+ NFS_MOUNTED=1
+
+ f_dprintf "Mounted NFS device %s onto %s" "$name" "$MOUNTPOINT"
+
+ return $SUCCESS
+}
+
+# f_media_get_nfs $device $file [$probe_type]
+#
+# Returns data from $file on a mounted NFS device. Similar to cat(1). If
+# $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_get_nfs()
+{
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_nfs: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_type"
+}
+
+# f_media_shutdown_nfs $device
+#
+# Shuts down the NFS device using umount(8). Return status should be ignored.
+#
+f_media_shutdown_nfs()
+{
+ local funcname=f_media_shutdown_nfs
+ local dev="$1" err
+
+ [ "$NFS_MOUNTED" ] || return $FAILURE
+
+ f_dprintf "Unmounting NFS partition on %s" "$MOUNTPOINT"
+ if ! f_eval_catch -dk err $funcname umount \
+ 'umount -f "%s"' "$MOUNTPOINT"
+ then
+ err="${err#umount: }"; err="${err#*: }"
+ f_show_msg "$msg_could_not_unmount_the_nfs_partition" \
+ "$MOUNTPOINT" "$err"
+ else
+ NFS_MOUNTED=
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/nfs.subr
+
+fi # ! $_MEDIA_NFS_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/options.subr b/usr.sbin/bsdconfig/share/media/options.subr
new file mode 100644
index 0000000..bc9568e
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/options.subr
@@ -0,0 +1,327 @@
+if [ ! "$_MEDIA_OPTIONS_SUBR" ]; then _MEDIA_OPTIONS_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/options.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/any.subr
+f_include $BSDCFG_SHARE/media/ftp.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+OPTIONS_HELPFILE=$BSDCFG_LIBE/include/options.hlp
+
+############################################################ FUNCTIONS
+
+# f_media_options_menu
+#
+# Prompt the user to confirm/edit various media settings. Returns success.
+#
+f_media_options_menu()
+{
+ f_dialog_title "$msg_options_editor"
+ local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
+ f_dialog_title_restore
+ local prompt=
+ local menu_list # Calculated below
+ local defaultitem=
+ local hline="$hline_arrows_tab_enter"
+
+ #
+ # A hack so that the dialogs below are always interactive in a script
+ #
+ local old_interactive=
+ if ! f_interactive; then
+ f_getvar $VAR_NONINTERACTIVE old_interactive
+ unset $VAR_NONINTERACTIVE
+ fi
+
+ local cp
+ while :; do
+ menu_list=
+
+ f_getvar $VAR_NFS_SECURE cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_nfs_secure' 'YES'
+ '$msg_nfs_server_talks_only_on_a_secure_port'"
+ else menu_list="$menu_list
+ ' $msg_nfs_secure' 'NO'
+ '$msg_nfs_server_talks_only_on_a_secure_port'"
+ fi
+
+ f_getvar $VAR_SLOW_ETHER cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_nfs_slow' 'YES'
+ '$msg_user_is_using_a_slow_pc_or_ethernet_card'"
+ else menu_list="$menu_list
+ ' $msg_nfs_slow' 'NO'
+ '$msg_user_is_using_a_slow_pc_or_ethernet_card'"
+ fi
+
+ f_getvar $VAR_NFS_TCP cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_nfs_tcp' 'YES' '$msg_use_tcp_protocol_for_nfs'"
+ else menu_list="$menu_list
+ ' $msg_nfs_tcp' 'NO' '$msg_use_tcp_protocol_for_nfs'"
+ fi
+
+ f_getvar $VAR_NFS_V3 cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_nfs_version_3' 'YES' '$msg_use_nfs_version_3'"
+ else menu_list="$menu_list
+ ' $msg_nfs_version_3' 'NO' '$msg_use_nfs_version_3'"
+ fi
+
+ f_getvar $VAR_DEBUG cp
+ if [ "$cp" ]; then menu_list="$menu_list
+ ' $msg_debugging' 'YES'
+ '$msg_emit_extra_debugging_output'"
+ else menu_list="$menu_list
+ ' $msg_debugging' 'NO'
+ '$msg_emit_extra_debugging_output'"
+ fi
+
+ f_getvar $VAR_NO_CONFIRM cp
+ if [ "$cp" ]; then menu_list="$menu_list
+ ' $msg_yes_to_all' 'YES'
+ '$msg_assume_yes_to_all_non_critical_dialogs'"
+ else menu_list="$menu_list
+ ' $msg_yes_to_all' 'NO'
+ '$msg_assume_yes_to_all_non_critical_dialogs'"
+ fi
+
+ f_getvar $VAR_TRY_DHCP cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_dhcp' 'YES'
+ '$msg_attempt_automatic_dhcp_configuration'"
+ else menu_list="$menu_list
+ ' $msg_dhcp' 'NO'
+ '$msg_attempt_automatic_dhcp_configuration'"
+ fi
+
+ f_getvar $VAR_TRY_RTSOL cp
+ if [ "$cp" = "YES" ]; then menu_list="$menu_list
+ ' $msg_ipv6' 'YES'
+ '$msg_attempt_ipv6_configuration_of_interfaces'"
+ else menu_list="$menu_list
+ ' $msg_ipv6' 'NO'
+ '$msg_attempt_ipv6_configuration_of_interfaces'"
+ fi
+
+ f_getvar $VAR_FTP_USER cp
+ menu_list="$menu_list
+ ' $msg_ftp_username' '$cp'
+ '$msg_username_and_password_to_use'"
+
+ f_getvar $VAR_EDITOR cp
+ menu_list="$menu_list
+ ' $msg_editor' '$cp' '$msg_which_text_editor_to_use'"
+
+ f_getvar $VAR_RELNAME cp
+ menu_list="$menu_list
+ ' $msg_release_name' '$cp'
+ '$msg_which_release_to_attempt_to_load'"
+
+ if f_struct device_media; then
+ device_media get type cp
+ case "$cp" in
+ $DEVICE_TYPE_UFS|$DEVICE_TYPE_DISK)
+ cp="$msg_file_system" ;;
+ $DEVICE_TYPE_DIRECTORY) cp="$msg_directory" ;;
+ $DEVICE_TYPE_FLOPPY) cp="$msg_floppy" ;;
+ $DEVICE_TYPE_FTP) cp="$msg_ftp" ;;
+ $DEVICE_TYPE_HTTP_PROXY) cp="$msg_http_proxy" ;;
+ $DEVICE_TYPE_HTTP) cp="$msg_http_direct" ;;
+ $DEVICE_TYPE_CDROM) cp="$msg_cdrom" ;;
+ $DEVICE_TYPE_USB) cp="$msg_usb" ;;
+ $DEVICE_TYPE_DOS) cp="$msg_dos" ;;
+ $DEVICE_TYPE_NFS) cp="$msg_nfs" ;;
+ *)
+ cp="<$msg_unknown>"
+ esac
+ else
+ cp="<$msg_not_yet_set>"
+ fi
+ menu_list="$menu_list
+ ' $msg_media_type' '$cp'
+ '$msg_the_current_installation_media_type'"
+
+ f_getvar $VAR_MEDIA_TIMEOUT cp
+ menu_list="$menu_list
+ ' $msg_media_timeout' '$cp'
+ '$msg_timeout_value_in_seconds_for_slow_media'"
+
+ f_getvar $VAR_PKG_TMPDIR cp
+ menu_list="$menu_list
+ ' $msg_package_temp' '$cp'
+ '$msg_directory_where_package_temporary_files_go'"
+
+ menu_list="$menu_list
+ ' $msg_rescan_devices' '<*>'
+ '$msg_rerun_bsdconfig_initial_device_probe'
+ ' $msg_use_defaults' '[${msg_reset}]'
+ '$msg_reset_all_values_to_startup_defaults'
+ " # END-QUOTE
+
+ local height width rows
+ eval f_dialog_menu_with_help_size height width rows \
+ \"\$title\" \
+ \"\$btitle\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local mtag
+ mtag=$( eval $DIALOG \
+ --title \"\$title\" \
+ --backtitle \"\$btitle\" \
+ --hline \"\$hline\" \
+ --item-help \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_done\" \
+ --help-button \
+ --help-label \"\$msg_help\" \
+ --default-item \"\$defaultitem\" \
+ ${USE_XDIALOG:+--help \"\"} \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_data_sanitize mtag
+ defaultitem="$mtag"
+ f_dprintf "retval=%s mtag=[%s]" $retval "$mtag"
+
+ if [ $retval -eq $DIALOG_HELP ]; then
+ f_show_help "$OPTIONS_HELPFILE"
+ continue
+ elif [ $retval -ne $DIALOG_OK ]; then
+ break # to success
+ fi
+
+ case "$mtag" in
+ " $msg_nfs_secure")
+ f_getvar $VAR_NFS_SECURE cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_NFS_SECURE="NO"
+ else
+ export $VAR_NFS_SECURE="YES"
+ fi ;;
+ " $msg_nfs_slow")
+ f_getvar $VAR_SLOW_ETHER cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_SLOW_ETHER="NO"
+ else
+ export $VAR_SLOW_ETHER="YES"
+ fi ;;
+ " $msg_nfs_tcp")
+ f_getvar $VAR_NFS_TCP cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_NFS_TCP="NO"
+ else
+ export $VAR_NFS_TCP="YES"
+ fi ;;
+ " $msg_nfs_version_3")
+ f_getvar $VAR_NFS_V3 cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_NFS_V3="NO"
+ else
+ export $VAR_NFS_V3="YES"
+ fi ;;
+ " $msg_debugging")
+ if f_getvar $VAR_DEBUG cp && [ "$cp" ]; then
+ unset $VAR_DEBUG
+ else
+ export $VAR_DEBUG=1
+ fi ;;
+ " $msg_yes_to_all")
+ if f_getvar $VAR_NO_CONFIRM cp && [ "$cp" ]; then
+ unset $VAR_NO_CONFIRM
+ else
+ export $VAR_NO_CONFIRM=1
+ fi ;;
+ " $msg_dhcp")
+ f_getvar $VAR_TRY_DHCP cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_TRY_DHCP="NO"
+ else
+ export $VAR_TRY_DHCP="YES"
+ fi ;;
+ " $msg_ipv6")
+ f_getvar $VAR_TRY_RTSOL cp
+ if [ "$cp" = "YES" ]; then
+ export $VAR_TRY_RTSOL="NO"
+ else
+ export $VAR_TRY_RTSOL="YES"
+ fi ;;
+ " $msg_ftp_username")
+ f_media_set_ftp_userpass ;;
+ " $msg_editor")
+ f_variable_get_value $VAR_EDITOR \
+ "$msg_please_specify_the_name_of_the_text_editor"
+ ;;
+ " $msg_release_name")
+ f_variable_get_value $VAR_RELNAME \
+ "$msg_please_specify_the_release_you_wish_to_load"
+ ;;
+ " $msg_media_type")
+ f_media_get_type ;;
+ " $msg_media_timeout")
+ f_variable_get_value $VAR_MEDIA_TIMEOUT \
+ "$msg_please_specify_the_number_of_seconds_to_wait"
+ ;;
+ " $msg_package_temp")
+ f_variable_get_value $VAR_PKG_TMPDIR \
+ "$msg_please_specify_a_temporary_directory"
+ ;;
+ " $msg_rescan_devices")
+ f_device_rescan ;;
+ " $msg_use_defaults")
+ f_variable_set_defaults ;;
+ esac
+ done
+
+ # Restore old VAR_NONINTERACTIVE if needed.
+ [ "$old_interactive" ] &&
+ setvar $VAR_NONINTERACTIVE "$old_interactive"
+
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/options.subr
+
+fi # ! $_MEDIA_OPTIONS_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/tcpip.subr b/usr.sbin/bsdconfig/share/media/tcpip.subr
new file mode 100644
index 0000000..42c6e20
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/tcpip.subr
@@ -0,0 +1,1713 @@
+if [ ! "$_MEDIA_TCPIP_SUBR" ]; then _MEDIA_TCPIP_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/tcpip.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/strings.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+TCP_HELPFILE=$BSDCFG_LIBE/include/tcp.hlp
+NETWORK_DEVICE_HELPFILE=$BSDCFG_LIBE/include/network_device.hlp
+
+############################################################ GLOBALS
+
+#
+# Path to resolv.conf(5).
+#
+: ${RESOLV_CONF:="/etc/resolv.conf"}
+
+#
+# Path to nsswitch.conf(5).
+#
+: ${NSSWITCH_CONF:="/etc/nsswitch.conf"}
+
+#
+# Path to hosts(5)
+#
+: ${ETC_HOSTS:="/etc/hosts"}
+
+#
+# Structure of dhclient.leases(5) lease { ... } entry
+#
+f_struct_define DHCP_LEASE \
+ interface \
+ fixed_address \
+ filename \
+ server_name \
+ script \
+ medium \
+ host_name \
+ subnet_mask \
+ routers \
+ domain_name_servers \
+ domain_name \
+ broadcast_address \
+ dhcp_lease_time \
+ dhcp_message_type \
+ dhcp_server_identifier \
+ dhcp_renewal_time \
+ dhcp_rebinding_time \
+ renew \
+ rebind \
+ expire
+
+############################################################ FUNCTIONS
+
+# f_validate_hostname $hostname
+#
+# Returns zero if the given argument (a fully-qualified hostname) is compliant
+# with standards set-forth in RFC's 952 and 1123 of the Network Working Group:
+#
+# RFC 952 - DoD Internet host table specification
+# http://tools.ietf.org/html/rfc952
+#
+# RFC 1123 - Requirements for Internet Hosts - Application and Support
+# http://tools.ietf.org/html/rfc1123
+#
+# See http://en.wikipedia.org/wiki/Hostname for a brief overview.
+#
+# The return status for invalid hostnames is one of:
+# 255 Entire hostname exceeds the maximum length of 255 characters.
+# 63 One or more individual labels within the hostname (separated by
+# dots) exceeds the maximum of 63 characters.
+# 1 One or more individual labels within the hostname contains one
+# or more invalid characters.
+# 2 One or more individual labels within the hostname starts or
+# ends with a hyphen (hyphens are allowed, but a label cannot
+# begin or end with a hyphen).
+# 3 One or more individual labels within the hostname are null.
+#
+# To call this function and display an appropriate error message to the user
+# based on the above error codes, use the following function defined in
+# dialog.subr:
+#
+# f_dialog_validate_hostname $hostname
+#
+f_validate_hostname()
+{
+ local fqhn="$1"
+
+ # Return error if the hostname exceeds 255 characters
+ [ ${#fqhn} -gt 255 ] && return 255
+
+ local IFS="." # Split on `dot'
+ for label in $fqhn; do
+ # Return error if the label exceeds 63 characters
+ [ ${#label} -gt 63 ] && return 63
+
+ # Return error if the label is null
+ [ "$label" ] || return 3
+
+ # Return error if label begins/ends with dash
+ case "$label" in -*|*-) return 2; esac
+
+ # Return error if the label contains any invalid chars
+ case "$label" in *[!0-9a-zA-Z-]*) return 1; esac
+ done
+
+ return $SUCCESS
+}
+
+# f_inet_atoi $ipv4_address [$var_to_set]
+#
+# Convert an IPv4 address or mask from dotted-quad notation (e.g., `127.0.0.1'
+# or `255.255.255.0') to a 32-bit unsigned integer for the purpose of network
+# and broadcast calculations. For example, one can validate that two addresses
+# are on the same network:
+#
+# f_inet_atoi 1.2.3.4 ip1num
+# f_inet_atoi 1.2.4.5 ip2num
+# f_inet_atoi 255.255.0.0 masknum
+# if [ $(( $ip1num & $masknum )) -eq \
+# $(( $ip2num & $masknum )) ]
+# then
+# : IP addresses are on same network
+# fi
+#
+# See f_validate_ipaddr() below for an additional example usage, on calculating
+# network and broadcast addresses.
+#
+# If $var_to_set is missing or NULL, the converted IP address 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_inet_atoi()
+{
+ local __addr="$1" __var_to_set="$2" __num=0
+ if f_validate_ipaddr "$__addr"; then
+ local IFS=.
+ set -- $__addr
+ __num=$(( ($1 << 24) + ($2 << 16) + ($3 << 8) + $4 ))
+ fi
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" $__num
+ else
+ echo $__num
+ fi
+}
+
+# f_validate_ipaddr $ipaddr [$netmask]
+#
+# Returns zero if the given argument (an IP address) is of the proper format.
+#
+# The return status for invalid IP address is one of:
+# 1 One or more individual octets within the IP address (separated
+# by dots) contains one or more invalid characters.
+# 2 One or more individual octets within the IP address are null
+# and/or missing.
+# 3 One or more individual octets within the IP address exceeds the
+# maximum of 255 (or 2^8, being an octet comprised of 8 bits).
+# 4 The IP address has either too few or too many octets.
+#
+# If a netmask is provided, the IP address is checked further:
+#
+# 5 The IP address must not be the network or broadcast address.
+#
+f_validate_ipaddr()
+{
+ local ip="$1" mask="$2"
+
+ # Track number of octets for error checking
+ local noctets=0
+
+ local oldIFS="$IFS" IFS="." # Split on `dot'
+ for octet in $ip; do
+ # Return error if the octet is null
+ [ "$octet" ] || return 2
+
+ # Return error if not a whole integer
+ f_isinteger "$octet" || return 1
+
+ # Return error if not a positive integer
+ [ $octet -ge 0 ] || return 1
+
+ # Return error if the octet exceeds 255
+ [ $octet -gt 255 ] && return 3
+
+ noctets=$(( $noctets + 1 ))
+ done
+ IFS="$oldIFS"
+
+ [ $noctets -eq 4 ] || return 4
+
+ #
+ # The IP address must not be network or broadcast address.
+ #
+ if [ "$mask" ]; then
+ local ipnum masknum netnum bcastnum
+ local max_addr=4294967295 # 255.255.255.255
+
+ f_inet_atoi $ip ipnum
+ f_inet_atoi $mask masknum
+
+ netnum=$(( $ipnum & $masknum ))
+ bcastnum=$(( ($ipnum & $masknum)+$max_addr-$masknum ))
+
+ if [ "$masknum" ] &&
+ [ $ipnum -eq $netnum -o $ipnum -eq $bcastnum ]
+ then
+ return 5
+ fi
+ fi
+
+ return $SUCCESS
+}
+
+# f_validate_ipaddr6 $ipv6_addr
+#
+# Returns zero if the given argument (an IPv6 address) is of the proper format.
+#
+# The return status for invalid IP address is one of:
+# 1 One or more individual segments within the IP address
+# (separated by colons) contains one or more invalid characters.
+# Segments must contain only combinations of the characters 0-9,
+# A-F, or a-f.
+# 2 Too many/incorrect null segments. A single null segment is
+# allowed within the IP address (separated by colons) but not
+# allowed at the beginning or end (unless a double-null segment;
+# i.e., "::*" or "*::").
+# 3 One or more individual segments within the IP address
+# (separated by colons) exceeds the length of 4 hex-digits.
+# 4 The IP address entered has either too few (less than 3), too
+# many (more than 8), or not enough segments, separated by
+# colons.
+# 5* The IPv4 address at the end of the IPv6 address is invalid.
+# * When there is an error with the dotted-quad IPv4 address at the
+# end of the IPv6 address, the return value of 5 is OR'd with a
+# bit-shifted (<< 4) return of f_validate_ipaddr.
+#
+f_validate_ipaddr6()
+{
+ local ip="${1%\%*}" # removing the interface specification if-present
+
+ local IFS=":" # Split on `colon'
+ set -- $ip:
+
+ # Return error if too many or too few segments
+ # Using 9 as max in case of leading or trailing null spanner
+ [ $# -gt 9 -o $# -lt 3 ] && return 4
+
+ local h="[0-9A-Fa-f]"
+ local nulls=0 nsegments=$# contains_ipv4_segment=
+
+ while [ $# -gt 0 ]; do
+
+ segment="${1%:}"
+ shift
+
+ #
+ # Return error if this segment makes one null too-many. A
+ # single null segment is allowed anywhere in the middle as well
+ # as double null segments are allowed at the beginning or end
+ # (but not both).
+ #
+ if [ ! "$segment" ]; then
+ nulls=$(( $nulls + 1 ))
+ if [ $nulls -eq 3 ]; then
+ # Only valid syntax for 3 nulls is `::'
+ [ "$ip" = "::" ] || return 2
+ elif [ $nulls -eq 2 ]; then
+ # Only valid if begins/ends with `::'
+ case "$ip" in
+ ::*|*::) : fall thru ;;
+ *) return 2
+ esac
+ fi
+ continue
+ fi
+
+ #
+ # Return error if not a valid hexadecimal short
+ #
+ case "$segment" in
+ $h|$h$h|$h$h$h|$h$h$h$h)
+ : valid segment of 1-4 hexadecimal digits
+ ;;
+ *[!0-9A-Fa-f]*)
+ # Segment contains at least one invalid char
+
+ # Return error immediately if not last segment
+ [ $# -eq 0 ] || return 1
+
+ # Otherwise, check for legacy IPv4 notation
+ case "$segment" in
+ *[!0-9.]*)
+ # Segment contains at least one invalid
+ # character even for an IPv4 address
+ return 1
+ esac
+
+ # Return error if not enough segments
+ if [ $nulls -eq 0 ]; then
+ [ $nsegments -eq 7 ] || return 4
+ fi
+
+ contains_ipv4_segment=1
+
+ # Validate the IPv4 address
+ f_validate_ipaddr "$segment" ||
+ return $(( 5 | $? << 4 ))
+ ;;
+ *)
+ # Segment characters are all valid but too many
+ return 3
+ esac
+
+ done
+
+ if [ $nulls -eq 1 ]; then
+ # Single null segment cannot be at beginning/end
+ case "$ip" in
+ :*|*:) return 2
+ esac
+ fi
+
+ #
+ # A legacy IPv4 address can span the last two 16-bit segments,
+ # reducing the amount of maximum allowable segments by-one.
+ #
+ maxsegments=8
+ if [ "$contains_ipv4_segment" ]; then
+ maxsegments=7
+ fi
+
+ case $nulls in
+ # Return error if missing segments with no null spanner
+ 0) [ $nsegments -eq $maxsegments ] || return 4 ;;
+ # Return error if null spanner with too many segments
+ 1) [ $nsegments -le $maxsegments ] || return 4 ;;
+ # Return error if leading/trailing `::' with too many segments
+ 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || return 4 ;;
+ esac
+
+ return $SUCCESS
+}
+
+# f_validate_netmask $netmask
+#
+# Returns zero if the given argument (a subnet mask) is of the proper format.
+#
+# The return status for invalid netmask is one of:
+# 1 One or more individual fields within the subnet mask (separated
+# by dots) contains one or more invalid characters.
+# 2 One or more individual fields within the subnet mask are null
+# and/or missing.
+# 3 One or more individual fields within the subnet mask exceeds
+# the maximum of 255 (a full 8-bit register).
+# 4 The subnet mask has either too few or too many fields.
+# 5 One or more individual fields within the subnet mask is an
+# invalid integer (only 0,128,192,224,240,248,252,254,255 are
+# valid integers).
+#
+f_validate_netmask()
+{
+ local mask="$1"
+
+ # Track number of fields for error checking
+ local nfields=0
+
+ local IFS="." # Split on `dot'
+ for field in $mask; do
+ # Return error if the field is null
+ [ "$field" ] || return 2
+
+ # Return error if not a whole positive integer
+ f_isinteger "$field" || return 1
+
+ # Return error if the field exceeds 255
+ [ $field -gt 255 ] && return 3
+
+ # Return error if the field is an invalid integer
+ case "$field" in
+ 0|128|192|224|240|248|252|254|255) : ;;
+ *) return 5 ;;
+ esac
+
+ nfields=$(( $nfields + 1 ))
+ done
+
+ [ $nfields -eq 4 ] || return 4
+}
+
+# f_validate_gateway $gateway $ipaddr $netmask
+#
+# Validate an IPv4 default gateway (aka router) address for a given IP address
+# making sure the two are in the same network (able to ``talk'' to each other).
+# Returns success if $ipaddr and $gateway are in the same network given subnet
+# mask $netmask.
+#
+f_validate_gateway()
+{
+ local gateway="$1" ipaddr="$2" netmask="$3"
+ local gwnum ipnum masknum
+
+ f_validate_ipaddr "$gateway" "$netmask" || return $FAILURE
+
+ f_inet_atoi "$netmask" masknum
+ f_inet_atoi "$ipaddr" ipnum
+ f_inet_atoi "$gateway" gwnum
+
+ # Gateway must be within set of IPs reachable through interface
+ [ $(( $ipnum & $masknum )) -eq \
+ $(( $gwnum & $masknum )) ] # Return status
+}
+
+# f_dialog_validate_tcpip $hostname $gateway $nameserver $ipaddr $netmask
+#
+# Returns success if the arguments provided are valid for accessing a TCP/IP
+# network, otherwise returns failure.
+#
+f_dialog_validate_tcpip()
+{
+ local hostname="$1" gateway="$2" nameserver="$3"
+ local ipaddr="$4" netmask="$5"
+ local ipnum masknum
+
+ if [ ! "$hostname" ]; then
+ f_show_msg "$msg_must_specify_a_host_name_of_some_sort"
+ elif ! f_validate_hostname "$hostname"; then
+ f_show_msg "$msg_invalid_hostname_value"
+ elif [ "$netmask" ] && ! f_validate_netmask "$netmask"; then
+ f_show_msg "$msg_invalid_netmask_value"
+ elif [ "$nameserver" ] &&
+ ! f_validate_ipaddr "$nameserver" &&
+ ! f_validate_ipaddr6 "$nameserver"; then
+ f_show_msg "$msg_invalid_name_server_ip_address_specified"
+ elif [ "$ipaddr" ] && ! f_validate_ipaddr "$ipaddr" "$netmask"; then
+ f_show_msg "$msg_invalid_ipv4_address"
+ elif [ "$gateway" -a "$gateway" != "NO" ] &&
+ ! f_validate_gateway "$gateway" "$ipaddr" "$netmask"; then
+ f_show_msg "$msg_invalid_gateway_ipv4_address_specified"
+ else
+ return $DIALOG_OK
+ fi
+
+ return $DIALOG_CANCEL
+}
+
+# f_ifconfig_inet $interface [$var_to_set]
+#
+# Returns the IPv4 address associated with $interface. If $var_to_set is
+# missing or NULL, the IP address 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).
+#
+# 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_ifconfig_inet_awk='
+BEGIN { found = 0 }
+( $1 == "inet" ) \
+{
+ print $2
+ found = 1
+ exit
+}
+END { exit ! found }
+'
+f_ifconfig_inet()
+{
+ local __interface="$1" __var_to_set="$2"
+ if [ "$__var_to_set" ]; then
+ local __ip
+ __ip=$( ifconfig "$__interface" 2> /dev/null |
+ awk "$f_ifconfig_inet_awk" )
+ setvar "$__var_to_set" "$__ip"
+ else
+ ifconfig "$__interface" 2> /dev/null |
+ awk "$f_ifconfig_inet_awk"
+ fi
+}
+
+# f_ifconfig_inet6 $interface [$var_to_set]
+#
+# Returns the IPv6 address associated with $interface. If $var_to_set is
+# missing or NULL, the IP address 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).
+#
+# 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_ifconfig_inet6_awk='
+BEGIN { found = 0 }
+( $1 == "inet6" ) \
+{
+ print $2
+ found = 1
+ exit
+}
+END { exit ! found }
+'
+f_ifconfig_inet6()
+{
+ local __interface="$1" __var_to_set="$2"
+ if [ "$__var_to_set" ]; then
+ local __ip6
+ __ip6=$( ifconfig "$__interface" 2> /dev/null |
+ awk "$f_ifconfig_inet6_awk" )
+ setvar "$__var_to_set" "$__ip6"
+ else
+ ifconfig "$__interface" 2> /dev/null |
+ awk "$f_ifconfig_inet6_awk"
+ fi
+}
+
+# f_ifconfig_netmask $interface [$var_to_set]
+#
+# Returns the IPv4 subnet mask associated with $interface. If $var_to_set is
+# missing or NULL, the netmask 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_ifconfig_netmask()
+{
+ local __interface="$1" __var_to_set="$2" __octets
+ __octets=$( ifconfig "$__interface" 2> /dev/null | awk \
+ '
+ BEGIN { found = 0 }
+ ( $1 == "inet" ) \
+ {
+ printf "%s %s %s %s\n",
+ substr($4,3,2),
+ substr($4,5,2),
+ substr($4,7,2),
+ substr($4,9,2)
+ found = 1
+ exit
+ }
+ END { exit ! found }
+ ' ) || return $FAILURE
+
+ local __octet __netmask=
+ for __octet in $__octets; do
+ f_sprintf __netmask "%s.%u" "$__netmask" "0x$__octet"
+ done
+ __netmask="${__netmask#.}"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__netmask"
+ else
+ echo $__netmask
+ fi
+}
+
+# f_route_get_default [$var_to_set]
+#
+# Returns the IP address of the currently active default router. If $var_to_set
+# is missing or NULL, the IP address 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).
+#
+# 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_route_get_default_awk='
+BEGIN { found = 0 }
+( $1 == "gateway:" ) \
+{
+ print $2
+ found = 1
+ exit
+}
+END { exit ! found }
+'
+f_route_get_default()
+{
+ local __var_to_set="$1"
+ if [ "$__var_to_set" ]; then
+ local __ip
+ __ip=$( route -n get default 2> /dev/null |
+ awk "$f_route_get_default_awk" )
+ setvar "$__var_to_set" "$__ip"
+ else
+ route -n get default 2> /dev/null |
+ awk "$f_route_get_default_awk"
+ fi
+}
+
+# f_resolv_conf_nameservers [$var_to_set]
+#
+# Returns nameserver(s) configured in resolv.conf(5). If $var_to_set is missing
+# or NULL, the list of nameservers 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).
+#
+# 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_resolv_conf_nameservers_awk='
+BEGIN { found = 0 }
+( $1 == "nameserver" ) \
+{
+ print $2
+ found = 1
+}
+END { exit ! found }
+'
+f_resolv_conf_nameservers()
+{
+ local __var_to_set="$1"
+ if [ "$__var_to_set" ]; then
+ local __ns
+ __ns=$( awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
+ 2> /dev/null )
+ setvar "$__var_to_set" "$__ns"
+ else
+ awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
+ 2> /dev/null
+ fi
+}
+
+# f_config_resolv
+#
+# Attempts to configure resolv.conf(5) and ilk. Returns success if able to
+# write the file(s), otherwise returns error status.
+#
+# Variables from variable.subr that are used in configuring resolv.conf(5) are
+# as follows (all of which can be configured automatically through functions
+# like f_dhcp_get_info() or manually):
+#
+# VAR_NAMESERVER
+# The nameserver to add in resolv.conf(5).
+# VAR_DOMAINNAME
+# The domain to configure in resolv.conf(5). Also used in the
+# configuration of hosts(5).
+# VAR_IPADDR
+# The IPv4 address to configure in hosts(5).
+# VAR_IPV6ADDR
+# The IPv6 address to configure in hosts(5).
+# VAR_HOSTNAME
+# The hostname to associate with the IPv4 and/or IPv6 address in
+# hosts(5).
+#
+f_config_resolv()
+{
+ local cp c6p dp hp
+
+ f_getvar $VAR_NAMESERVER cp
+ if [ "$cp" ]; then
+ case "$RESOLV_CONF" in
+ */*) f_quietly mkdir -p "${RESOLV_CONF%/*}" ;;
+ esac
+
+ # Attempt to create/truncate the file
+ ( :> "$RESOLV_CONF" ) 2> /dev/null || return $FAILURE
+
+ f_getvar $VAR_DOMAINNAME dp &&
+ printf "domain\t%s\n" "$dp" >> "$RESOLV_CONF"
+ printf "nameserver\t%s\n" "$cp" >> "$RESOLV_CONF"
+
+ f_dprintf "Wrote out %s" "$RESOLV_CONF"
+ fi
+
+ f_getvar $VAR_DOMAINNAME dp
+ f_getvar $VAR_IPADDR cp
+ f_getvar $VAR_IPV6ADDR c6p
+ f_getvar $VAR_HOSTNAME hp
+
+ # Attempt to create the file if it doesn't already exist
+ if [ ! -e "$ETC_HOSTS" ]; then
+ case "$ETC_HOSTS" in
+ */*) f_quietly mkdir -p "${ETC_HOSTS%/*}" ;;
+ esac
+
+ ( :> "$ETC_HOSTS" ) 2> /dev/null || return $FAILURE
+ fi
+
+ # Scan the file and add ourselves if not already configured
+ awk -v dn="$dp" -v ip4="$cp" -v ip6="$c6p" -v hn="$hp" '
+ BEGIN {
+ local4found = local6found = 0
+ hn4found = hn6found = h4found = h6found = 0
+ h = ( match(hn, /\./) ? substr(hn, 0, RSTART-1) : "" )
+ }
+ ($1 == "127.0.0.1") { local4found = 1 }
+ ($1 == "::1") { local6found = 1 }
+ {
+ for (n = 2; n <= NF; n++)
+ {
+ if ( $1 == ip4 ) {
+ if ( $n == h ) h4found = 1
+ if ( $n == hn ) hn4found = 1
+ if ( $n == hn "." ) hn4found = 1
+ }
+ if ( $1 == ip6 ) {
+ if ( $n == h ) h6found = 1
+ if ( $n == hn ) hn6found = 1
+ if ( $n == hn "." ) hn6found = 1
+ }
+ }
+ }
+ END {
+ hosts = FILENAME
+
+ if ( ! local6found )
+ printf "::1\t\t\tlocalhost%s\n",
+ ( dn ? " localhost." dn : "" ) >> hosts
+ if ( ! local4found )
+ printf "127.0.0.1\t\tlocalhost%s\n",
+ ( dn ? " localhost." dn : "" ) >> hosts
+
+ if ( ip6 && ! (h6found && hn6found))
+ {
+ printf "%s\t%s %s\n", ip6, hn, h >> hosts
+ printf "%s\t%s.\n", ip6, hn >> hosts
+ }
+ else if ( ip6 )
+ {
+ if ( ! h6found )
+ printf "%s\t%s.\n", ip6, h >> hosts
+ if ( ! hn6found )
+ printf "%s\t%s\n", ip6, hn >> hosts
+ }
+
+ if ( ip4 && ! (h4found && hn4found))
+ {
+ printf "%s\t\t%s %s\n", ip4, hn, h >> hosts
+ printf "%s\t\t%s.\n", ip4, hn >> hosts
+ }
+ else if ( ip4 )
+ {
+ if ( ! h4found )
+ printf "%s\t\t%s.\n", ip4, h >> hosts
+ if ( ! hn4found )
+ printf "%s\t\t%s\n", ip4, hn >> hosts
+ }
+ }
+ ' "$ETC_HOSTS" 2> /dev/null || return $FAILURE
+
+ f_dprintf "Wrote out %s" "$ETC_HOSTS"
+ return $SUCCESS
+}
+
+# f_dhcp_parse_leases $leasefile struct_name
+#
+# Parse $leasefile and store the information for the most recent lease in a
+# struct (see struct.subr for additional details) named `struct_name'. See
+# DHCP_LEASE struct definition in the GLOBALS section above.
+#
+f_dhcp_parse_leases()
+{
+ local leasefile="$1" struct_name="$2"
+
+ [ "$struct_name" ] || return $FAILURE
+
+ if [ ! -e "$leasefile" ]; then
+ f_dprintf "%s: No such file or directory" "$leasefile"
+ return $FAILURE
+ fi
+
+ f_struct "$struct_name" && f_struct_free "$struct_name"
+ f_struct_new DHCP_LEASE "$struct_name"
+
+ eval "$( awk -v struct="$struct_name" '
+ BEGIN {
+ lease_found = 0
+ keyword_list = " \
+ interface \
+ fixed-address \
+ filename \
+ server-name \
+ script \
+ medium \
+ "
+ split(keyword_list, keywords, FS)
+
+ time_list = "renew rebind expire"
+ split(time_list, times, FS)
+
+ option_list = " \
+ host-name \
+ subnet-mask \
+ routers \
+ domain-name-servers \
+ domain-name \
+ broadcast-address \
+ dhcp-lease-time \
+ dhcp-message-type \
+ dhcp-server-identifier \
+ dhcp-renewal-time \
+ dhcp-rebinding-time \
+ "
+ split(option_list, options, FS)
+ }
+ function set_value(prop,value)
+ {
+ lease_found = 1
+ gsub(/[^[:alnum:]_]/, "_", prop)
+ sub(/;$/, "", value)
+ sub(/^"/, "", value)
+ sub(/"$/, "", value)
+ sub(/,.*/, "", value)
+ printf "%s set %s \"%s\"\n", struct, prop, value
+ }
+ /^lease {$/, /^}$/ \
+ {
+ if ( $0 ~ /^lease {$/ ) next
+ if ( $0 ~ /^}$/ ) exit
+
+ for (k in keywords)
+ {
+ keyword = keywords[k]
+ if ( $1 == keyword )
+ {
+ set_value(keyword, $2)
+ next
+ }
+ }
+
+ for (t in times)
+ {
+ time = times[t]
+ if ( $1 == time )
+ {
+ set_value(time, $2 " " $3 " " $4)
+ next
+ }
+ }
+
+ if ( $1 != "option" ) next
+ for (o in options)
+ {
+ option = options[o]
+ if ( $2 == option )
+ {
+ set_value(option, $3)
+ next
+ }
+ }
+ }
+ EXIT {
+ if ( ! lease_found )
+ {
+ printf "f_struct_free \"%s\"\n", struct
+ print "return $FAILURE"
+ }
+ }
+ ' "$leasefile" )"
+}
+
+# f_dhcp_get_info $interface
+#
+# Parse the dhclient(8) lease database for $interface to obtain all the
+# necessary IPv4 details necessary to communicate on the network. The retrieved
+# information is stored in VAR_IPADDR, VAR_NETMASK, VAR_GATEWAY, and
+# VAR_NAMESERVER.
+#
+# If reading the lease database fails, values are obtained from ifconfig(8) and
+# route(8). If the DHCP lease did not provide a nameserver (or likewise, we
+# were unable to parse the lease database), fall-back to resolv.conf(5) for
+# obtaining the nameserver. Always returns success.
+#
+f_dhcp_get_info()
+{
+ local interface="$1" cp
+ local leasefile="/var/db/dhclient.leases.$interface"
+
+ # If it fails, do it the old-fashioned way
+ if f_dhcp_parse_leases "$leasefile" lease; then
+ lease get fixed_address $VAR_IPADDR
+ lease get subnet_mask $VAR_NETMASK
+ lease get routers cp
+ setvar $VAR_GATEWAY "${cp%%,*}"
+ lease get domain_name_servers cp
+ setvar $VAR_NAMESERVER "${cp%%,*}"
+ lease get host_name cp &&
+ setvar $VAR_HOSTNAME "$cp"
+ f_struct_free lease
+ else
+ # Bah, now we have to get the information from ifconfig
+ if f_debugging; then
+ f_dprintf "DHCP configured interface returns %s" \
+ "$( ifconfig "$interface" )"
+ fi
+ f_ifconfig_inet "$interface" $VAR_IPADDR
+ f_ifconfig_netmask "$interface" $VAR_NETMASK
+ f_route_get_default $VAR_GATEWAY
+ fi
+
+ # If we didn't get a name server value, hunt for it in resolv.conf
+ local ns
+ if [ -r "$RESOLV_CONF" ] && ! {
+ f_getvar $VAR_NAMESERVER ns || [ "$ns" ]
+ }; then
+ f_resolv_conf_nameservers cp &&
+ setvar $VAR_NAMESERVER ${cp%%[$IFS]*}
+ fi
+
+ return $SUCCESS
+}
+
+# f_rtsol_get_info $interface
+#
+# Returns the rtsol-provided IPv6 address associated with $interface. The
+# retrieved IP address is stored in VAR_IPV6ADDR. Always returns success.
+#
+f_rtsol_get_info()
+{
+ local interface="$1" cp
+ cp=$( ifconfig "$interface" 2> /dev/null | awk \
+ '
+ BEGIN { found = 0 }
+ ( $1 == "inet6" ) && ( $2 ~ /^fe80:/ ) \
+ {
+ print $2
+ found = 1
+ exit
+ }
+ END { exit ! found }
+ ' ) && setvar $VAR_IPV6ADDR "$cp"
+}
+
+# f_host_lookup $host [$var_to_set]
+#
+# Use host(1) to lookup (or reverse) an Internet number from (or to) a name.
+# Multiple answers are returned separated by a single space. If host(1) does
+# not exit cleanly, its full output is provided and the return status is 1.
+#
+# If nsswitch.conf(5) has been configured to query local access first for the
+# `hosts' database, we'll manually check hosts(5) first (preventing host(1)
+# from hanging in the event that DNS goes awry).
+#
+# If $var_to_set is missing or NULL, the list of IP addresses 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).
+#
+# The variables from variable.subr used in looking up the host are as follows
+# (which are set manually):
+#
+# VAR_IPV6_ENABLE [Optional]
+# If set to "YES", enables the lookup of IPv6 addresses and IPv4
+# address. IPv6 addresses, if any, will come before IPv4. Note
+# that if nsswitch.conf(5) shows an affinity for "files" for the
+# "host" database and there is a valid entry in hosts(5) for
+# $host, this setting currently has no effect (an IPv4 address
+# can supersede an IPv6 address). By design, hosts(5) overrides
+# any preferential treatment. Otherwise, if this variable is not
+# set, IPv6 addresses will not be used (IPv4 addresses will
+# specifically be requested from DNS).
+#
+# 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_host_lookup_awk='
+BEGIN{ addrs = "" }
+!/^[[:space:]]*(#|$)/ \
+{
+ for (n=1; n++ < NF;) if ($n == name)
+ addrs = addrs (addrs ? " " : "") $1
+}
+END {
+ if (addrs) print addrs
+ exit !addrs
+}
+'
+f_host_lookup()
+{
+ local __host="$1" __var_to_set="$2"
+ f_dprintf "f_host_lookup: host=[%s]" "$__host"
+
+ # If we're configured to look at local files first, do that
+ if awk '/^hosts:/{exit !($2=="files")}' "$NSSWITCH_CONF"; then
+ if [ "$__var_to_set" ]; then
+ local __cp
+ if __cp=$( awk -v name="$__host" \
+ "$f_host_lookup_awk" "$ETC_HOSTS" )
+ then
+ setvar "$__var_to_set" "$__cp"
+ return $SUCCESS
+ fi
+ else
+ awk -v name="$__host" \
+ "$f_host_lookup_awk" "$ETC_HOSTS" &&
+ return $SUCCESS
+ fi
+ fi
+
+ #
+ # Fall back to host(1) -- which is further governed by nsswitch.conf(5)
+ #
+
+ local __output __ip6 __addrs=
+ f_getvar $VAR_IPV6_ENABLE __ip6
+
+ # If we have a TCP media type configured, check for an SRV record
+ local __srvtypes=
+ { f_quietly f_getvar $VAR_HTTP_PATH ||
+ f_quietly f_getvar $VAR_HTTP_PROXY_PATH
+ } && __srvtypes="$__srvtypes _http._tcp"
+ f_quietly f_getvar $VAR_FTP_PATH && __srvtypes="$__srvtypes _ftp._tcp"
+ f_quietly f_getvar $VAR_NFS_PATH &&
+ __srvtypes="$__srvtypes _nfs._tcp _nfs._udp"
+
+ # Calculate wait time as dividend of total time and host(1) invocations
+ local __host_runs __wait
+ f_count __host_runs $__srvtypes
+ if [ "$__ip6" = "YES" ]; then
+ __host_runs=$(( $__host_runs + 2 ))
+ else
+ __host_runs=$(( $__host_runs + 1 ))
+ fi
+ f_getvar $VAR_MEDIA_TIMEOUT __wait
+ [ "$__wait" ] && __wait="-W $(( $__wait / $__host_runs ))"
+
+ # Query SRV types first (1st host response taken as new host to query)
+ for __type in $__srvtypes; do
+ if __output=$(
+ host -t SRV $__wait -- "$__type.$__host" \
+ 2> /dev/null
+ ); then
+ __host=$( echo "$__output" |
+ awk '/ SRV /{print $NF;exit}' )
+ break
+ fi
+ done
+
+ # Try IPv6 first (if enabled)
+ if [ "$__ip6" = "YES" ]; then
+ if ! __output=$( host -t AAAA $__wait -- "$__host" 2>&1 ); then
+ # An error occurred, display in-full and return error
+ [ "$__var_to_set" ] &&
+ setvar "$__var_to_set" "$__output"
+ return $FAILURE
+ fi
+ # Add the IPv6 addresses and fall-through to collect IPv4 too
+ __addrs=$( echo "$__output" | awk '/ address /{print $NF}' )
+ fi
+
+ # Good ol' IPv4
+ if ! __output=$( host -t A $__wait -- "$__host" 2>&1 ); then
+ # An error occurred, display it in-full and return error
+ [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output"
+ return $FAILURE
+ fi
+
+ __addrs="$__addrs${__addrs:+ }$(
+ echo "$__output" | awk '/ address /{print $NF}' )"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__addrs"
+ else
+ echo $__addrs
+ fi
+}
+
+# f_device_dialog_tcp $device
+#
+# This is it - how to get TCP setup values. Prompt the user to edit/confirm the
+# interface, gateway, nameserver, and hostname settings -- all required for
+# general TCP/IP access.
+#
+# Variables from variable.subr that can be used to sript user input:
+#
+# VAR_NO_INET6
+# If set, prevents asking the user if they would like to use
+# rtsol(8) to check for an IPv6 router.
+# VAR_TRY_RTSOL
+# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
+# user if they would like to try the IPv6 RouTer SOLicitation
+# utility (rtsol(8)) to get IPv6 information. Ignored if
+# VAR_NO_INET6 is set.
+# VAR_TRY_DHCP
+# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
+# user if they would like to try to acquire IPv4 connection
+# settings from a DHCP server using dhclient(8).
+#
+# VAR_GATEWAY Default gateway to use.
+# VAR_IPADDR Interface address to assign.
+# VAR_NETMASK Interface subnet mask.
+# VAR_EXTRAS Extra interface options to ifconfig(8).
+# VAR_HOSTNAME Hostname to set.
+# VAR_DOMAINNAME Domain name to use.
+# VAR_NAMESERVER DNS nameserver to use when making lookups.
+# VAR_IPV6ADDR IPv6 interface address.
+#
+# In addition, the following variables are used in acquiring network settings
+# from the user:
+#
+# VAR_NONINTERACTIVE
+# If set (such as when running in a script), prevents asking the
+# user questions or displaying the usual prompts, etc.
+# VAR_NETINTERACTIVE
+# The one exception to VAR_NONINTERACTIVE is VAR_NETINTERACTIVE,
+# which if set will prompt the user to try RTSOL (unless
+# VAR_TRY_RTSOL has been set), try DHCP (unless VAR_TRY_DHCP has
+# been set), and display the network verification dialog. This
+# allows you to have a mostly non-interactive script that still
+# prompts for network setup/confirmation.
+#
+# After successfull execution, the following variables are set:
+#
+# VAR_IFCONFIG + $device (e.g., `ifconfig_em0')
+# Defines the ifconfig(8) properties specific to $device.
+#
+f_device_dialog_tcp()
+{
+ local dev="$1" devname cp n
+ local use_dhcp="" use_rtsol=""
+ local _ipaddr _netmask _extras
+
+ [ "$dev" ] || return $DIALOG_CANCEL
+ f_struct "$dev" get name devname || return $DIALOG_CANCEL
+
+ # Initialize vars from previous device values
+ local private
+ $dev get private private
+ if [ "$private" ] && f_struct "$private"; then
+ $private get ipaddr _ipaddr
+ $private get netmask _netmask
+ $private get extras _extras
+ $private get use_dhcp use_dhcp
+ $private get use_rtsol use_rtsol
+ else # See if there are any defaults
+
+ #
+ # This is a hack so that the dialogs below are interactive in a
+ # script if we have requested interactive behavior.
+ #
+ local old_interactive=
+ if ! f_interactive && f_netinteractive; then
+ f_getvar $VAR_NONINTERACTIVE old_interactive
+ unset $VAR_NONINTERACTIVE
+ fi
+
+ #
+ # Try a RTSOL scan if such behavior is desired.
+ # If the variable was configured and is YES, do it.
+ # If it was configured to anything else, treat it as NO.
+ # Otherwise, ask the question interactively.
+ #
+ local try6
+ if ! f_isset $VAR_NO_INET6 && {
+ { f_getvar $VAR_TRY_RTSOL try6 && [ "$try6" = "YES" ]; } ||
+ {
+ # Only prompt the user when VAR_TRY_RTSOL is unset
+ ! f_isset $VAR_TRY_RTSOL &&
+ f_dialog_noyes "$msg_try_ipv6_configuration"
+ }
+ }; then
+ local i
+
+ f_quietly sysctl net.inet6.ip6.forwarding=0
+ f_quietly sysctl net.inet6.ip6.accept_rtadv=1
+ f_quietly ifconfig $devname up
+
+ i=$( sysctl -n net.inet6.ip6.dad_count )
+ sleep $(( $i + 1 ))
+
+ f_quietly mkdir -p /var/run
+ f_dialog_info "$msg_scanning_for_ra_servers"
+ if f_quietly rtsol $devname; then
+ i=$( sysctl -n net.inet6.ip6.dad_count )
+ sleep $(( $i + 1 ))
+ f_rtsol_get_info $devname
+ use_rtsol=1
+ else
+ use_rtsol=
+ fi
+ fi
+
+ #
+ # Try a DHCP scan if such behavior is desired.
+ # If the variable was configured and is YES, do it.
+ # If it was configured to anything else, treat it as NO.
+ # Otherwise, ask the question interactively.
+ #
+ local try4
+ if { f_getvar $VAR_TRY_DHCP try4 && [ "$try4" = "YES" ]; } || {
+ # Only prompt the user when VAR_TRY_DHCP is unset
+ ! f_isset $VAR_TRY_DHCP &&
+ f_dialog_noyes "$msg_try_dhcp_configuration"
+ }; then
+ f_quietly ifconfig $devname delete
+ f_quietly mkdir -p /var/db
+ f_quietly mkdir -p /var/run
+ f_quietly mkdir -p /tmp
+
+ local msg="$msg_scanning_for_dhcp_servers"
+ trap - SIGINT
+ ( # Execute in sub-shell to allow/catch Ctrl-C
+ trap 'exit $FAILURE' SIGINT
+ if [ "$USE_XDIALOG" ]; then
+ f_quietly dhclient $devname |
+ f_xdialog_info "$msg"
+ else
+ f_dialog_info "$msg"
+ f_quietly dhclient $devname
+ fi
+ )
+ local retval=$?
+ trap 'f_interrupt' SIGINT
+ if [ $retval -eq $SUCCESS ]; then
+ f_dhcp_get_info $devname
+ use_dhcp=1
+ else
+ use_dhcp=
+ fi
+ fi
+
+ # Restore old VAR_NONINTERACTIVE if needed.
+ [ "$old_interactive" ] &&
+ setvar $VAR_NONINTERACTIVE "$old_interactive"
+
+ # Special hack so it doesn't show up oddly in the menu
+ local gw
+ if f_getvar $VAR_GATEWAY gw && [ "$gw" = "NO" ]; then
+ setvar $VAR_GATEWAY ""
+ fi
+
+ # Get old IP address from variable space, if available
+ if [ ! "$_ipaddr" ]; then
+ if f_getvar $VAR_IPADDR cp; then
+ _ipaddr="$cp"
+ elif f_getvar ${devname}_$VAR_IPADDR cp; then
+ _ipaddr="$cp"
+ fi
+ fi
+
+ # Get old netmask from variable space, if available
+ if [ ! "$_netmask" ]; then
+ if f_getvar $VAR_NETMASK cp; then
+ _netmask="$cp"
+ elif f_getvar ${devname}_$VAR_NETMASK cp; then
+ _netmask="$cp"
+ fi
+ fi
+
+ # Get old extras string from variable space, if available
+ if [ ! "$_extras" ]; then
+ if f_getvar $VAR_EXTRAS cp; then
+ _extras="$cp"
+ elif f_getvar ${devname}_$VAR_EXTRAS cp; then
+ _extras="$cp"
+ fi
+ fi
+ fi
+
+ # Look up values already recorded with the system, or blank the string
+ # variables ready to accept some new data
+ local _hostname _gateway _nameserver
+ f_getvar $VAR_HOSTNAME _hostname
+ case "$_hostname" in
+ *.*) : do nothing ;; # Already fully-qualified
+ *)
+ f_getvar $VAR_DOMAINNAME cp
+ [ "$cp" ] && _hostname="$_hostname.$cp"
+ esac
+ f_getvar $VAR_GATEWAY _gateway
+ f_getvar $VAR_NAMESERVER _nameserver
+
+ # Re-check variables for initial inheritance before heading into dialog
+ [ "$_hostname" ] || _hostname="${HOSTNAME:-$( hostname )}"
+ [ "$_gateway" ] || f_route_get_default _gateway
+ [ ! "$_nameserver" ] &&
+ f_resolv_conf_nameservers cp && _nameserver=${cp%%[$IFS]*}
+ [ "$_ipaddr" ] || f_ifconfig_inet $devname _ipaddr
+ [ "$_netmask" ] || f_ifconfig_netmask $devname _netmask
+
+ # If non-interactive, jump over dialog section and into config section
+ if f_netinteractive || f_interactive || [ ! "$_hostname" ]
+ then
+ [ ! "$_hostname" ] && f_interactive &&
+ f_show_msg "$msg_hostname_variable_not_set"
+
+ local title=" $msg_network_configuration "
+ local hline="$hline_alnum_arrows_punc_tab_enter"
+ local extras_help="$tcplayout_extras_help"
+
+ # Modify the help line for PLIP config
+ [ "${devname#plip}" != "$devname" ] &&
+ extras_help="$tcplayout_extras_help_for_plip"
+
+ f_getvar $VAR_IPV6ADDR cp && [ "$cp" ] &&
+ title="$title($msg_ipv6_ready) "
+
+ if [ ! "$USE_XDIALOG" ]; then
+ local prompt="$msg_dialog_mixedform_navigation_help"
+ # Calculate center position for displaying device label
+ local devlabel="$msg_configuration_for_interface"
+ devlabel="$devlabel $devname"
+ local width=54
+ local n=$(( $width/2 - (${#devlabel} + 4)/2 - 2 ))
+
+ while :; do
+ cp=$( $DIALOG \
+ --title "$title" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --item-help \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --help-button \
+ --help-label "$msg_help" \
+ --mixedform "$prompt" 16 $width 9 \
+ "$msg_host_name_including_domain:" 1 2 \
+ "$_hostname" 2 3 45 255 0 \
+ "$tcplayout_hostname_help" \
+ "$msg_ipv4_gateway:" 3 2 \
+ "$_gateway" 4 3 16 15 0 \
+ "$tcplayout_gateway_help" \
+ "$msg_name_server:" 3 31 \
+ "$_nameserver" 4 32 16 15 0 \
+ "$tcplayout_nameserver_help" \
+ "- $devlabel -" 5 $n "" 0 0 0 0 3 "" \
+ "$msg_ipv4_address:" 6 6 \
+ "$_ipaddr" 7 7 16 15 0 \
+ "$tcplayout_ipaddr_help" \
+ "$msg_netmask:" 6 31 \
+ "$_netmask" 7 32 16 15 0 \
+ "$tcplayout_netmask_help" \
+ "$msg_extra_options_to_ifconfig" 8 6 \
+ "$_extras" 9 7 41 2048 0 \
+ "$extras_help" \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD )
+
+ # --mixed-form always returns 0, we have to
+ # use the returned data to determine button
+ if [ ! "$cp" ]; then
+ # User either chose "Cancel", pressed
+ # ESC, or blanked every form field
+ return $DIALOG_CANCEL
+ else
+ n=$( echo "$cp" | f_number_of_lines )
+ [ $n -eq 1 ] && case "$cp" in HELP*)
+ # User chose "Help"
+ f_show_help "$TCP_HELPFILE"
+ continue
+ esac
+ fi
+
+ # Turn mixed-form results into env variables
+ eval "$( echo "$cp" | awk '
+ BEGIN {
+ n = 0
+ field[++n] = "_hostname"
+ field[++n] = "_gateway"
+ field[++n] = "_nameserver"
+ field[++n] = "_ipaddr"
+ field[++n] = "_netmask"
+ field[++n] = "_extras"
+ nfields = n
+ n = 0
+ }
+ {
+ gsub(/'\''/, "'\'\\\\\'\''")
+ sub(/[[:space:]]*$/, "")
+ value[field[++n]] = $0
+ }
+ END {
+ for ( n = 1; n <= nfields; n++ )
+ {
+ printf "%s='\''%s'\'';\n",
+ field[n],
+ value[field[n]]
+ }
+ }' )"
+
+ f_dialog_validate_tcpip \
+ "$_hostname" \
+ "$_gateway" \
+ "$_nameserver" \
+ "$_ipaddr" \
+ "$_netmask" \
+ && break
+ done
+ else
+ # Xdialog(1) does not support --mixed-form
+ # Create a persistent menu instead
+
+ f_dialog_title "$msg_network_configuration"
+ local prompt=
+
+ while :; do
+ cp=$( $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --item-help \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --help "" \
+ --menu "$prompt" 21 60 8 \
+ "$msg_accept_continue" "" \
+ "$tcplayout_accept_cont_help" \
+ "$msg_host_name_including_domain:" \
+ "$_hostname" \
+ "$tcplayout_hostname_help" \
+ "$msg_ipv4_gateway:" "$_gateway" \
+ "$tcplayout_gateway_help" \
+ "$msg_name_server:" "$_nameserver" \
+ "$tcplayout_nameserver_help" \
+ "$msg_ipv4_address:" "$_ipaddr" \
+ "$tcplayout_ipaddr_help" \
+ "$msg_netmask:" "$_netmask" \
+ "$tcplayout_netmask_help" \
+ "$msg_extra_options_to_ifconfig" \
+ "$_extras" "$extras_help" \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_data_sanitize cp
+ f_dprintf "retval=%u mtag=[%s]" $retval "$cp"
+
+ if [ $retval -eq $DIALOG_HELP ]; then
+ f_show_help "$TCP_HELPFILE"
+ continue
+ elif [ $retval -ne $DIALOG_OK ]; then
+ f_dialog_title_restore
+ return $DIALOG_CANCEL
+ fi
+
+ case "$cp" in
+ "$msg_accept_continue")
+ f_dialog_validate_tcpip \
+ "$_hostname" \
+ "$_gateway" \
+ "$_nameserver" \
+ "$_ipaddr" \
+ "$_netmask" \
+ && break ;;
+ "$msg_host_name_including_domain:")
+ f_dialog_input cp "$cp" "$_hostname" \
+ && _hostname="$cp" ;;
+ "$msg_ipv4_gateway:")
+ f_dialog_input cp "$cp" "$_gateway" \
+ && _gateway="$cp" ;;
+ "$msg_name_server:")
+ f_dialog_input cp "$cp" "$_nameserver" \
+ && _nameserver="$cp" ;;
+ "$msg_ipv4_address:")
+ f_dialog_input cp "$cp" "$_ipaddr" \
+ && _ipaddr="$cp" ;;
+ "$msg_netmask:")
+ f_dialog_input cp "$cp" "$_netmask" \
+ && _netmask="$cp" ;;
+ "$msg_extra_options_to_ifconfig")
+ f_dialog_input cp "$cp" "$_extras" \
+ && _extras="$cp" ;;
+ esac
+ done
+
+ f_dialog_title_restore
+
+ fi # XDIALOG
+
+ fi # interactive
+
+ # We actually need to inform the rest of bsdconfig about this
+ # data now if the user hasn't selected cancel.
+
+ if [ "$_hostname" ]; then
+ setvar $VAR_HOSTNAME "$_hostname"
+ f_quietly hostname "$_hostname"
+ case "$_hostname" in
+ *.*) setvar $VAR_DOMAINNAME "${_hostname#*.}" ;;
+ esac
+ fi
+ [ "$_gateway" ] && setvar $VAR_GATEWAY "$_gateway"
+ [ "$_nameserver" ] && setvar $VAR_NAMESERVER "$_nameserver"
+ [ "$_ipaddr" ] && setvar $VAR_IPADDR "$_ipaddr"
+ [ "$_netmask" ] && setvar $VAR_NETMASK "$_netmask"
+ [ "$_extras" ] && setvar $VAR_EXTRAS "$_extras"
+
+ f_dprintf "Creating struct DEVICE_INFO devinfo_%s" "$dev"
+ f_struct_new DEVICE_INFO devinfo_$dev
+ $dev set private devinfo_$dev
+
+ devinfo_$dev set ipaddr $_ipaddr
+ devinfo_$dev set netmask $_netmask
+ devinfo_$dev set extras $_extras
+ devinfo_$dev set use_rtsol $use_rtsol
+ devinfo_$dev set use_dhcp $use_dhcp
+
+ if [ "$use_dhcp" -o "$_ipaddr" ]; then
+ if [ "$use_dhcp" ]; then
+ cp="DHCP${extras:+ $extras}"
+ else
+ cp="inet $_ipaddr netmask $_netmask${extras:+ $extras}"
+ fi
+ setvar $VAR_IFCONFIG$devname "$cp"
+ fi
+ [ "$use_rtsol" ] &&
+ setvar $VAR_IPV6_ENABLE "YES"
+
+ [ "$use_dhcp" ] ||
+ f_config_resolv # XXX this will do it on the MFS copy
+
+ return $DIALOG_OK
+}
+
+# f_device_scan_tcp [$var_to_set]
+#
+# Scan for the first active/configured TCP/IP device. The name of the interface
+# is printed to stderr like other dialog(1)-based functions (stdout is reserved
+# for dialog(1) interaction) if $var_to_set is missing or NULL. Returns failure
+# if no active/configured interface
+#
+f_device_scan_tcp()
+{
+ local __var_to_set="$1" __iface
+ for __iface in $( ifconfig -l ); do
+ if ifconfig $__iface | awk '
+ BEGIN {
+ has_inet = has_inet6 = is_ethernet = 0
+ is_usable = 1
+ }
+ ( $1 == "status:" && $2 != "active" ) { is_usable = 0; exit }
+ ( $1 == "inet" ) {
+ if ($2 == "0.0.0.0") { is_usable = 0; exit }
+ has_inet++
+ }
+ ( $1 == "inet6") { has_inet6++ }
+ ( $1 == "media:" ) {
+ if ($2 != "Ethernet") { is_usable = 0; exit }
+ is_ethernet = 1
+ }
+ END {
+ if (!(is_ethernet && (has_inet || has_inet6)))
+ is_usable = 0
+ exit ! is_usable
+ }'; then
+ f_interactive &&
+ f_show_msg "$msg_using_interface" "$__iface"
+ f_dprintf "f_device_scan_tcp found %s" "$__iface"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__iface"
+ else
+ echo "$__iface" >&2
+ fi
+ return $SUCCESS
+ fi
+ done
+
+ return $FAILURE
+}
+
+# f_device_select_tcp
+#
+# Prompt the user to select network interface to use for TCP/IP access.
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_NETWORK_DEVICE [Optional]
+# Either a comma-separated list of network interfaces to try when
+# setting up network access (e.g., "fxp0,em0") or "ANY" (case-
+# sensitive) to indicate that the first active and configured
+# interface is acceptable. If unset, the user is presented with a
+# menu of all available network interfaces.
+#
+# Returns success if a valid network interface has been selected.
+#
+f_device_select_tcp()
+{
+ local devs dev cnt if network_dev
+ f_getvar $VAR_NETWORK_DEVICE network_dev
+
+ f_dprintf "f_device_select_tcp: %s=[%s]" \
+ VAR_NETWORK_DEVICE "$network_dev"
+
+ if [ "$network_dev" ]; then
+ #
+ # This can be set to several types of values. If set to ANY,
+ # scan all network devices looking for a valid link, and go
+ # with the first device found. Can also be specified as a
+ # comma delimited list, with each network device tried in
+ # order. Can also be set to a single network device.
+ #
+ [ "$network_dev" = "ANY" ] && f_device_scan_tcp network_dev
+
+ while [ "$network_dev" ]; do
+ case "$network_dev" in
+ *,*) if="${network_dev%%,*}"
+ network_dev="${network_dev#*,}"
+ ;;
+ *) if="$network_dev"
+ network_dev=
+ esac
+
+ f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
+ f_device_dialog_tcp $dev
+ if [ $? -eq $DIALOG_OK ]; then
+ setvar $VAR_NETWORK_DEVICE $if
+ return $DIALOG_OK
+ fi
+ done
+
+ f_interactive && f_show_msg "$msg_no_network_devices"
+ return $DIALOG_CANCEL
+
+ fi # $network_dev
+
+ f_device_find "" $DEVICE_TYPE_NETWORK devs
+ f_count cnt $devs
+ dev="${devs%%[$IFS]*}"
+ $dev get name if
+
+ f_quietly f_getvar NETWORK_CONFIGURED # for debugging info
+ if ! f_running_as_init &&
+ ! [ "${NETWORK_CONFIGURED+set}" -a "$NETWORK_CONFIGURED" = "NO" ]
+ then
+ trap 'f_interrupt' SIGINT
+ if f_dialog_yesno "$msg_assume_network_is_already_configured"
+ then
+ setvar $VAR_NETWORK_DEVICE $if
+ return $DIALOG_OK
+ fi
+ fi
+
+ local retval=$SUCCESS
+ if [ ${cnt:=0} -eq 0 ]; then
+ f_show_msg "$msg_no_network_devices"
+ retval=$DIALOG_CANCEL
+ elif [ $cnt -eq 1 ]; then
+ f_device_dialog_tcp $dev
+ retval=$?
+ [ $retval -eq $DIALOG_OK ] && setvar $VAR_NETWORK_DEVICE $if
+ else
+ local title="$msg_network_interface_information_required"
+ local prompt="$msg_please_select_ethernet_device_to_configure"
+ local hline="$hline_arrows_tab_enter"
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_NETWORK \
+ "$NETWORK_DEVICE_HELPFILE" \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $DIALOG_CANCEL
+
+ f_device_dialog_tcp $dev
+ retval=$?
+ if [ $retval -eq $DIALOG_OK ]; then
+ f_struct_copy "$dev" device_network
+ setvar $VAR_NETWORK_DEVICE device_network
+ else
+ f_struct_free device_network
+ fi
+ fi
+
+ return $retval
+}
+
+# f_dialog_menu_select_tcp
+#
+# Like f_dialog_select_tcp() above, but do it from a menu that doesn't care
+# about status. In other words, where f_dialog_select_tcp() will not display a
+# menu if scripted, this function will always display the menu of available
+# network interfaces.
+#
+f_dialog_menu_select_tcp()
+{
+ local private use_dhcp name
+ NETWORK_CONFIGURED=NO f_device_select_tcp
+ if f_struct device_network &&
+ device_network get private private &&
+ f_struct_copy "$private" di &&
+ di get use_dhcp use_dhcp &&
+ [ ! "$use_dhcp" ] &&
+ device_network get name name &&
+ f_yesno "$msg_would_you_like_to_bring_interface_up" "$name"
+ then
+ if ! f_device_init device_network; then
+ f_show_msg "$msg_initialization_of_device_failed" \
+ "$name"
+ fi
+ fi
+ return $DIALOG_OK
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/tcpip.subr
+
+fi # ! $_MEDIA_TCPIP_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/ufs.subr b/usr.sbin/bsdconfig/share/media/ufs.subr
new file mode 100644
index 0000000..27e2f27
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/ufs.subr
@@ -0,0 +1,198 @@
+if [ ! "$_MEDIA_UFS_SUBR" ]; then _MEDIA_UFS_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/ufs.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+UFS_MOUNTED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_ufs
+#
+# Return success if we both found and set the media type to be a UFS partition.
+# Variables from variable.subr that can be used to script user input:
+#
+# VAR_UFS_PATH
+# Path to a UFS character device node to be used with mount(8) in
+# mounting a UFS formatted partition. Valid examples include:
+# /dev/da0s1a
+# /dev/ad4s1e
+# However, other forms may be valid (see mount(8) for additional
+# information).
+#
+f_media_set_ufs()
+{
+ local ufs
+
+ f_media_close
+
+ local devs ndevs
+ f_device_find "" $DEVICE_TYPE_UFS devs
+ f_count ndevs $devs
+
+ if [ ${ndevs:=0} -eq 0 ]; then
+ f_variable_get_value $VAR_UFS_PATH \
+ "$msg_enter_the_device_name_of_a_ufs_formatted_partition"
+ f_getvar $VAR_UFS_PATH ufs
+ [ "$ufs" ] || return $FAILURE
+
+ local fstype
+ fstype=$( df -nT $ufs 2> /dev/null |
+ awk '!/Type/{print $2;exit}' )
+
+ f_struct_new DEVICE device_ufs
+ device_ufs set name ${fstype:-ufs}
+ device_ufs set devname "$ufs"
+ device_ufs set get f_media_get_ufs
+ device_ufs set init f_media_init_ufs
+ device_ufs set shutdown f_media_shutdown_ufs
+ device_ufs unset private
+
+ f_struct_copy device_ufs device_media
+ f_struct_free device_ufs
+ elif [ $ndevs -eq 1 ]; then
+ f_struct_copy $devs device_media
+ else
+ local dev
+ local title="$msg_choose_a_ufs_partition"
+ local prompt="$msg_please_select_ufs_partition"
+ local hline=""
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_UFS \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $FAILURE
+
+ f_struct_copy "$dev" device_media
+ fi
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_ufs $device
+#
+# Initializes the UFS media device. Returns success if able to mount the UFS
+# partition device using mount(1).
+#
+f_media_init_ufs()
+{
+ local funcname=f_media_init_ufs
+ local dev="$1" devname err
+
+ $dev get devname devname || return $FAILURE
+ f_dprintf "Init routine called for UFS device. devname=[%s]" \
+ "$devname"
+
+ if [ "$UFS_MOUNTED" ]; then
+ f_dprintf "UFS device already mounted."
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$devname" ]; then
+ f_show_msg "$msg_no_such_file_or_directory" \
+ "f_media_init_ufs" "$devname"
+ return $FAILURE
+ fi
+
+ if [ ! -e "$MOUNTPOINT" ]; then
+ f_eval_catch $funcname mkdir 'mkdir -p "%s"' "$MOUNTPOINT" ||
+ return $FAILURE
+ fi
+
+ if ! f_eval_catch -dk err $funcname mount \
+ 'mount "%s" "%s"' "$devname" "$MOUNTPOINT"
+ then
+ err="${err#mount: }"; err="${err#$devname : }"
+ f_show_msg "$msg_error_mounting_device" \
+ "$devname" "$MOUNTPOINT" "$err"
+ return $FAILURE
+ fi
+ UFS_MOUNTED=1
+ return $SUCCESS
+}
+
+# f_media_get_ufs $device $file [$probe_type]
+#
+# Returns data from $file on a mounted UFS partition device. Similar to cat(1).
+# If $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_get_ufs()
+{
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_ufs: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_type"
+}
+
+# f_media_shutdown_ufs $device
+#
+# Shuts down the UFS device using umount(8). Return status should be ignored.
+#
+f_media_shutdown_ufs()
+{
+ local funcname=f_media_shutdown_ufs
+ local dev="$1" err
+
+ [ "$UFS_MOUNTED" ] || return $FAILURE
+
+ if ! f_eval_catch -dk err $funcname umount \
+ 'umount -f "%s"' "$MOUNTPOINT"
+ then
+ err="${err#umount: }"; err="${err#*: }"
+ f_show_msg "$msg_could_not_unmount_the_ufs_partition" \
+ "$MOUNTPOINT" "$err"
+ else
+ UFS_MOUNTED=
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/ufs.subr
+
+fi # ! $_MEDIA_UFS_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/usb.subr b/usr.sbin/bsdconfig/share/media/usb.subr
new file mode 100644
index 0000000..5ee9bc0
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/media/usb.subr
@@ -0,0 +1,176 @@
+if [ ! "$_MEDIA_USB_SUBR" ]; then _MEDIA_USB_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." media/usb.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/struct.subr
+f_include $BSDCFG_SHARE/variable.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+USB_MOUNTED=
+
+############################################################ FUNCTIONS
+
+# f_media_set_usb
+#
+# Attempt to use USB as the media type. Return success if we both found and set
+# the media type to be a USB drive.
+#
+f_media_set_usb()
+{
+ f_media_close
+
+ local devs ndevs
+ f_device_find "" $DEVICE_TYPE_USB devs
+ f_count ndevs $devs
+
+ if [ ${ndevs:=0} -eq 0 ]; then
+ f_show_msg "$msg_no_usb_devices_found"
+ return $FAILURE
+ elif [ $ndevs -eq 1 ]; then
+ f_struct_copy $devs device_media
+ else
+ local dev
+ local title="$msg_choose_a_usb_drive"
+ local prompt="$msg_please_select_a_usb_drive"
+ local hline=
+
+ dev=$( f_device_menu \
+ "$title" "$prompt" "$hline" $DEVICE_TYPE_USB \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
+ return $FAILURE
+
+ f_struct_copy "$dev" device_media
+ fi
+
+ f_struct device_media &&
+ device_media unset private
+
+ if f_interactive; then
+ local name
+ f_struct device_media get name name
+ f_show_msg "$msg_using_usb_device" "$name"
+ fi
+
+ f_struct device_media || return $FAILURE
+}
+
+# f_media_init_usb $device
+#
+# Initializes the USB media device. Returns success if able to mount the USB
+# disk device using mount(8).
+#
+f_media_init_usb()
+{
+ local funcname=f_media_init_usb
+ local dev="$1" devname err
+
+ $dev get devname devname || return $FAILURE
+ f_dprintf "Init routine called for USB device. devname=[%s]" \
+ "$devname"
+
+ if [ "$USB_MOUNTED" ]; then
+ f_dprintf "USB device already mounted."
+ return $SUCCESS
+ fi
+
+ if [ ! -e "$MOUNTPOINT" ]; then
+ f_eval_catch $funcname mkdir 'mkdir -p "%s"' "$MOUNTPOINT" ||
+ return $FAILURE
+ fi
+
+ if f_eval_catch -dk err $funcname mount \
+ 'mount "%s" "%s"' "$devname" "$MOUNTPOINT"
+ then
+ USB_MOUNTED=1
+ return $SUCCESS
+ fi
+
+ err="${err#mount: }"; err="${err#$devname: }"
+ f_show_msg "$msg_error_mounting_usb_drive" \
+ "$devname" "$MOUNTPOINT" "$err"
+ return $FAILURE
+}
+
+# f_media_get_usb $device $file [$probe_type]
+#
+# Returns data from $file on a mounted USB disk device. Similar to cat(1). If
+# $probe_type is present and non-NULL, returns success if $file exists. If
+# $probe_type is equal to $PROBE_SIZE, prints the size of $file in bytes to
+# standard-out.
+#
+f_media_get_usb()
+{
+ local dev="$1" file="$2" probe_type="$3"
+ local name
+
+ $dev get name name
+ f_dprintf "f_media_get_usb: dev=[%s] file=[%s] probe_type=%s" \
+ "$name" "$file" "$probe_type"
+
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_type"
+}
+
+# f_media_shutdown_usb $device
+#
+# Shuts down the USB disk device using umount(8). Return status should be
+# ignored.
+#
+f_media_shutdown_usb()
+{
+ local funcname=f_media_shutdown_usb
+ local dev="$1" err
+
+ [ "$USB_MOUNTED" ] || return $FAILURE
+
+ if ! f_eval_catch -dk err $funcname umount \
+ 'umount -f "%s"' "$MOUNTPOINT"
+ then
+ err="${err#umount: }"; err="${err#*: }"
+ f_show_msg "$msg_could_not_unmount_the_ufs_partition" \
+ "$MOUNTPOINT" "$err"
+ else
+ USB_MOUNTED=
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." media/usb.subr
+
+fi # ! $_MEDIA_USB_SUBR
diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr
new file mode 100644
index 0000000..88ff818
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/mustberoot.subr
@@ -0,0 +1,424 @@
+if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
+#
+# Copyright (c) 2006-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." mustberoot.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/strings.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+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 funcname=f_become_root_via_sudo
+ local prompt hline height width rows msg
+
+ [ "$( id -u )" = "0" ] && return $SUCCESS
+
+ f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
+
+ #
+ # Ask the user if it's OK to become root via sudo(8) and give them
+ # the option to save this preference (by touch(1)ing a file in the
+ # user's $HOME directory).
+ #
+ local checkpath="${HOME%/}/.bsdconfig_uses_sudo"
+ if [ ! -e "$checkpath" ]; then
+ f_sprintf prompt "$msg_you_are_not_root_but" bsdconfig
+ f_sprintf msg "$msg_always_try_sudo_when_run_as" "$USER"
+ local menu_list="
+ 'X' '$msg_cancel_exit'
+ '1' '$msg'
+ '2' '$msg_try_sudo_only_this_once'
+ " # END-QUOTE
+ hline="$hline_arrows_tab_enter"
+
+ eval f_dialog_menu_size height width rows \
+ \"\$DIALOG_TITLE\" \
+ \"\$DIALOG_BACKTITLE\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ local mtag
+ mtag=$( eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_ok\" \
+ --cancel-label \"\$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ ) || f_die
+ f_dialog_data_sanitize mtag
+
+ case "$mtag" in
+ X) # Cancel/Exit
+ f_die ;;
+ 1) # Always try sudo(8) when run as $user
+ f_eval_catch $funcname touch \
+ 'touch "%s"' "$checkpath" &&
+ f_show_msg "$msg_created_path" "$checkpath"
+ esac
+ else
+ #
+ # This user has created the path signing-off on sudo(8)-use
+ # but let's still give them a short/quick/unobtrusive reminder
+ #
+ f_dialog_info "$msg_becoming_root_via_sudo"
+ [ "$USE_XDIALOG" ] || sleep 0.6
+ fi
+
+ #
+ # 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.
+ #
+ prompt="$msg_please_enter_password"
+ hline="$hline_alnum_punc_tab_enter"
+ f_dialog_inputbox_size height width \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$prompt" \
+ "$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 "$prompt" \
+ $height $width \
+ 2>&1 > /dev/null
+ )
+ retval=$?
+
+ # Catch X11-related errors
+ if [ $retval -eq $DIALOG_ESC ]; then
+ f_die $retval "$password"
+ elif [ $retval -ne $DIALOG_OK ]; then
+ # User cancelled
+ exit $retval
+ fi
+ else
+ password=$( $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --hline "$hline" \
+ --ok-label "$msg_ok" \
+ --cancel-label "$msg_cancel" \
+ --insecure \
+ --passwordbox "$prompt" \
+ $height $width \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ ) || exit $?
+ fi
+ debug= f_dialog_line_sanitize password
+
+ #
+ # 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
+ f_sprintf msg "$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)
+ f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr
+ 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 height width
+
+ 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"
+ f_xdialog_2inputsbox_size height width \
+ "$DIALOG_TITLE" \
+ "$DIALOG_BACKTITLE" \
+ "$msg" \
+ "$field_username" "" \
+ "$field_password" ""
+ 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 $DIALOG_ESC ] && f_die $retval "$user_pass"
+
+ # Exit if the user cancelled.
+ [ $retval -eq $DIALOG_OK ] || 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_show_msg "$msg_no_username"
+ continue
+ fi
+ if [ ! "$SECURE_ALLOW_ROOT" ]; then
+ case "$user" in
+ root|toor)
+ nfailures=$(( $nfailures + 1 ))
+ f_show_msg "$msg_user_disallowed" "$user"
+ continue
+ esac
+ fi
+ if ! f_quietly id "$user"; then
+ nfailures=$(( $nfailures + 1 ))
+ if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
+ f_show_msg "$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
+ f_sprintf msg "$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
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." mustberoot.subr
+
+fi # ! $_MUSTBEROOT_SUBR
diff --git a/usr.sbin/bsdconfig/share/packages/Makefile b/usr.sbin/bsdconfig/share/packages/Makefile
new file mode 100644
index 0000000..bc0e59a
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+FILESDIR= ${SHAREDIR}/bsdconfig/packages
+FILES= categories.subr index.subr musthavepkg.subr packages.subr
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsdconfig/share/packages/Makefile.depend b/usr.sbin/bsdconfig/share/packages/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/bsdconfig/share/packages/categories.subr b/usr.sbin/bsdconfig/share/packages/categories.subr
new file mode 100644
index 0000000..474c41d
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/categories.subr
@@ -0,0 +1,209 @@
+if [ ! "$_PACKAGES_CATEGORIES_SUBR" ]; then _PACKAGES_CATEGORIES_SUBR=1
+#
+# Copyright (c) 2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." packages/categories.subr
+f_include $BSDCFG_SHARE/strings.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+CATEGORIES=
+NCATEGORIES=0
+
+############################################################ FUNCTIONS
+
+# f_category_desc_get $category [$var_to_set]
+#
+# Fetch the description of a given category. Returns success if a match was
+# found, otherwise failure.
+#
+# If $var_to_set is missing or NULL, the category description is printed to
+# standard out for capturing in a sub-shell (which is less-recommended because
+# of performance degredation; for example, when called in a loop).
+#
+f_category_desc_get()
+{
+ local __category="$1" __var_to_set="$2" __cat __varcat
+
+ # Return failure if $category
+ [ "$__category" ] || return $FAILURE
+
+ for __cat in $CATEGORIES; do
+ [ "$__cat" = "$__category" ] || continue
+ f_str2varname $__cat __varcat
+ f_getvar _category_$__varcat $__var_to_set
+ return $?
+ done
+ return $FAILURE
+}
+
+# f_category_desc_set $category $desc
+#
+# Store a description in-association with a category. $category should be
+# alphanumeric and can include the underscore [_] but should not contain
+# whitespace. Returns success unless $category is NULL or no arguments. Use the
+# f_category_desc_get() routine with the same $category to retrieve the stored
+# description.
+#
+f_category_desc_set()
+{
+ local category="$1" desc="$2"
+ local cat varcat found=
+ [ "$category" ] || return $FAILURE
+ for cat in $CATEGORIES; do
+ [ "$cat" = "$category" ] || continue
+ f_str2varname $cat varcat
+ f_isset _category_$varcat || continue
+ found=1 && break
+ done
+ if [ ! "$found" ]; then
+ CATEGORIES="$CATEGORIES $category"
+ fi
+ f_str2varname $category varcat
+ setvar "_category_$varcat" "$desc"
+ # Export the variable for awk(1) ENVIRON visibility
+ export "_category_$varcat"
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+#
+# Load descriptions for package categories. Note that we don't internationalize
+# category names because this would be confusing for people used to browsing
+# the FTP mirrors or are otherwise familiar with an interface that does not
+# provide internationalized names. The descriptions can be used to provide i18n
+# users a description of the non-i18n category name.
+#
+f_category() { f_category_desc_set "$1" "$2"; }
+f_category All "$msg_all_desc"
+f_category accessibility "$msg_accessibility_desc"
+f_category afterstep "$msg_afterstep_desc"
+f_category arabic "$msg_arabic_desc"
+f_category archivers "$msg_archivers_desc"
+f_category astro "$msg_astro_desc"
+f_category audio "$msg_audio_desc"
+f_category benchmarks "$msg_benchmarks_desc"
+f_category biology "$msg_biology_desc"
+f_category cad "$msg_cad_desc"
+f_category chinese "$msg_chinese_desc"
+f_category comms "$msg_comms_desc"
+f_category converters "$msg_converters_desc"
+f_category databases "$msg_databases_desc"
+f_category deskutils "$msg_deskutils_desc"
+f_category devel "$msg_devel_desc"
+f_category dns "$msg_dns_desc"
+f_category docs "$msg_docs_desc"
+f_category editors "$msg_editors_desc"
+f_category elisp "$msg_elisp_desc"
+f_category emulators "$msg_emulators_desc"
+f_category enlightenment "$msg_enlightenment_desc"
+f_category finance "$msg_finance_desc"
+f_category french "$msg_french_desc"
+f_category ftp "$msg_ftp_desc"
+f_category games "$msg_games_desc"
+f_category geography "$msg_geography_desc"
+f_category german "$msg_german_desc"
+f_category gnome "$msg_gnome_desc"
+f_category gnustep "$msg_gnustep_desc"
+f_category graphics "$msg_graphics_desc"
+f_category hamradio "$msg_hamradio_desc"
+f_category haskell "$msg_haskell_desc"
+f_category hebrew "$msg_hebrew_desc"
+f_category hungarian "$msg_hungarian_desc"
+f_category ipv6 "$msg_ipv6_desc"
+f_category irc "$msg_irc_desc"
+f_category japanese "$msg_japanese_desc"
+f_category java "$msg_java_desc"
+f_category kde "$msg_kde_desc"
+f_category kld "$msg_kld_desc"
+f_category korean "$msg_korean_desc"
+f_category lang "$msg_lang_desc"
+f_category linux "$msg_linux_desc"
+f_category lisp "$msg_lisp_desc"
+f_category mail "$msg_mail_desc"
+f_category math "$msg_math_desc"
+f_category mbone "$msg_mbone_desc"
+f_category misc "$msg_misc_desc"
+f_category multimedia "$msg_multimedia_desc"
+f_category net "$msg_net_desc"
+f_category net-im "$msg_net_im_desc"
+f_category net-mgmt "$msg_net_mgmt_desc"
+f_category net-p2p "$msg_net_p2p_desc"
+f_category news "$msg_news_desc"
+f_category palm "$msg_palm_desc"
+f_category parallel "$msg_parallel_desc"
+f_category pear "$msg_pear_desc"
+f_category perl5 "$msg_perl5_desc"
+f_category plan9 "$msg_plan9_desc"
+f_category polish "$msg_polish_desc"
+f_category ports-mgmt "$msg_ports_mgmt_desc"
+f_category portuguese "$msg_portuguese_desc"
+f_category print "$msg_print_desc"
+f_category python "$msg_python_desc"
+f_category ruby "$msg_ruby_desc"
+f_category rubygems "$msg_rubygems_desc"
+f_category russian "$msg_russian_desc"
+f_category scheme "$msg_scheme_desc"
+f_category science "$msg_science_desc"
+f_category security "$msg_security_desc"
+f_category shells "$msg_shells_desc"
+f_category spanish "$msg_spanish_desc"
+f_category sysutils "$msg_sysutils_desc"
+f_category tcl "$msg_tcl_desc"
+f_category textproc "$msg_textproc_desc"
+f_category tk "$msg_tk_desc"
+f_category ukrainian "$msg_ukrainian_desc"
+f_category vietnamese "$msg_vietnamese_desc"
+f_category windowmaker "$msg_windowmaker_desc"
+f_category www "$msg_www_desc"
+f_category x11 "$msg_x11_desc"
+f_category x11-clocks "$msg_x11_clocks_desc"
+f_category x11-drivers "$msg_x11_drivers_desc"
+f_category x11-fm "$msg_x11_fm_desc"
+f_category x11-fonts "$msg_x11_fonts_desc"
+f_category x11-servers "$msg_x11_servers_desc"
+f_category x11-themes "$msg_x11_themes_desc"
+f_category x11-toolkits "$msg_x11_toolkits_desc"
+f_category x11-wm "$msg_x11_wm_desc"
+f_category xfce "$msg_xfce_desc"
+f_category zope "$msg_zope_desc"
+
+f_count NCATEGORIES $CATEGORIES
+f_dprintf "%s: Initialized %u package category descriptions." \
+ packages/categories.subr $NCATEGORIES
+
+f_dprintf "%s: Successfully loaded." packages/categories.subr
+
+fi # ! $_PACKAGES_CATEGORIES_SUBR
diff --git a/usr.sbin/bsdconfig/share/packages/index.subr b/usr.sbin/bsdconfig/share/packages/index.subr
new file mode 100644
index 0000000..f3c1713
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/index.subr
@@ -0,0 +1,414 @@
+if [ ! "$_PACKAGES_INDEX_SUBR" ]; then _PACKAGES_INDEX_SUBR=1
+#
+# Copyright (c) 2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." packages/index.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/packages/musthavepkg.subr
+f_include $BSDCFG_SHARE/strings.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ GLOBALS
+
+PACKAGE_INDEX=
+_INDEX_INITTED=
+
+#
+# Default path to pkg(8) repo-packagesite.sqlite database
+#
+SQLITE_REPO="/var/db/pkg/repo-packagesite.sqlite"
+
+#
+# Default path to on-disk cache INDEX file
+#
+PACKAGES_INDEX_CACHEFILE="/var/run/bsdconfig/packages_INDEX.cache"
+
+############################################################ FUNCTIONS
+
+# f_index_initialize [$var_to_set]
+#
+# Read and initialize the global index. Returns success unless media cannot be
+# initialized for any reason (e.g. user cancels media selection dialog or an
+# error occurs). The index is sorted before being loaded into $var_to_set.
+#
+# NOTE: The index is processed with f_index_read() [below] after being loaded.
+#
+f_index_initialize()
+{
+ local __funcname=f_index_initialize
+ local __var_to_set="${1:-PACKAGE_INDEX}"
+
+ [ "$_INDEX_INITTED" ] && return $SUCCESS
+
+ # Got any media?
+ f_media_verify || return $FAILURE
+
+ # Make sure we have a usable pkg(8) with $PKG_ABI
+ f_musthavepkg_init
+
+ # Does it move when you kick it?
+ f_device_init device_media || return $FAILURE
+
+ f_show_info "$msg_attempting_to_update_repository_catalogue"
+
+ #
+ # Generate $PACKAGESITE variable for pkg(8) based on media type
+ #
+ local __type __data __site
+ device_media get type __type
+ device_media get private __data
+ case "$__type" in
+ $DEVICE_TYPE_DIRECTORY)
+ __site="file://$__data/packages/$PKG_ABI" ;;
+ $DEVICE_TYPE_FLOPPY)
+ __site="file://${__data:-$MOUNTPOINT}/packages/$PKG_ABI" ;;
+ $DEVICE_TYPE_FTP)
+ f_getvar $VAR_FTP_PATH __site
+ __site="$__site/packages/$PKG_ABI" ;;
+ $DEVICE_TYPE_HTTP)
+ f_getvar $VAR_HTTP_PATH __site
+ __site="$__site/$PKG_ABI/latest" ;;
+ $DEVICE_TYPE_HTTP_PROXY)
+ f_getvar $VAR_HTTP_PROXY_PATH __site
+ __site="$__site/packages/$PKG_ABI" ;;
+ $DEVICE_TYPE_CDROM)
+ __site="file://$MOUNTPOINT/packages/$PKG_ABI"
+ export REPOS_DIR="$MOUNTPOINT/packages/repos" ;;
+ *) # UFS, DISK, CDROM, USB, DOS, NFS, etc.
+ __site="file://$MOUNTPOINT/packages/$PKG_ABI"
+ esac
+
+ f_dprintf "PACKAGESITE=[%s]" "$__site"
+ if ! f_eval_catch $__funcname pkg \
+ 'PACKAGESITE="%s" pkg update' "$__site"
+ then
+ f_show_err "$msg_unable_to_update_pkg_from_selected_media"
+ f_device_shutdown device_media
+ return $FAILURE
+ fi
+
+ #
+ # Try to get contents from validated on-disk cache
+ #
+
+ #
+ # Calculate digest used to determine if the on-disk persistant cache
+ # INDEX (containing this digest on the first line) is valid and can be
+ # used to quickly populate the environment.
+ #
+ local __sqlite_digest
+ if ! __sqlite_digest=$( md5 < "$SQLITE_REPO" 2> /dev/null ); then
+ f_show_err "$msg_no_pkg_database_found"
+ f_device_shutdown device_media
+ return $FAILURE
+ fi
+
+ #
+ # Check to see if the persistant cache INDEX file exists
+ #
+ if [ -f "$PACKAGES_INDEX_CACHEFILE" ]; then
+ #
+ # Attempt to populate the environment with the (soon to be)
+ # validated on-disk cache. If validation fails, fall-back to
+ # generating a fresh cache.
+ #
+ if eval $__var_to_set='$(
+ ( # Get digest as the first word on first line
+ read digest rest_ignored
+
+ #
+ # If the stored digest matches the calculated-
+ # one populate the environment from the on-disk
+ # cache and provide success exit status.
+ #
+ if [ "$digest" = "$__sqlite_digest" ]; then
+ cat
+ exit $SUCCESS
+ else
+ # Otherwise, return the current value
+ eval echo \"\$__var_to_set\"
+ exit $FAILURE
+ fi
+ ) < "$PACKAGES_INDEX_CACHEFILE" 2> /dev/null
+ )'; then
+ f_show_info \
+ "$msg_located_index_now_reading_package_data_from_it"
+ if ! f_index_read "$__var_to_set"; then
+ f_show_err \
+ "$msg_io_or_format_error_on_index_file"
+ return $FAILURE
+ fi
+ _INDEX_INITTED=1
+ return $SUCCESS
+ fi
+ # Otherwise, fall-thru to create a fresh cache from scratch
+ fi
+
+ #
+ # If we reach this point, we need to generate the data from scratch
+ #
+
+ f_show_info "$msg_generating_index_from_pkg_database"
+ eval "$__var_to_set"='$( pkg rquery -I | sort )'
+
+ #
+ # Attempt to create the persistant on-disk cache
+ #
+
+ # Create a new temporary file to write to
+ local __tmpfile
+ if f_eval_catch -dk __tmpfile $__funcname mktemp \
+ 'mktemp -t "%s"' "$pgm"
+ then
+ # Write the temporary file contents
+ echo "$__sqlite_digest" > "$__tmpfile"
+ debug= f_getvar "$__var_to_set" >> "$__tmpfile"
+
+ # Finally, move the temporary file into place
+ case "$PACKAGES_INDEX_CACHEFILE" in
+ */*) f_eval_catch -d $__funcname mkdir \
+ 'mkdir -p "%s"' "${PACKAGES_INDEX_CACHEFILE%/*}"
+ esac
+ f_eval_catch -d $__funcname mv 'mv -f "%s" "%s"' \
+ "$__tmpfile" "$PACKAGES_INDEX_CACHEFILE"
+ fi
+
+ f_show_info "$msg_located_index_now_reading_package_data_from_it"
+ if ! f_index_read "$__var_to_set"; then
+ f_show_err "$msg_io_or_format_error_on_index_file"
+ return $FAILURE
+ fi
+
+ _INDEX_INITTED=1
+ return $SUCCESS
+}
+
+# f_index_read [$var_to_get]
+#
+# Process the INDEX file (contents contained in $var_to_get) and...
+#
+# 1. create a list ($CATEGORY_MENU_LIST) of categories with package counts
+# 2. For convenience, create $_npkgs holding the total number of all packages
+# 3. extract associative categories for each package into $_categories_$varpkg
+# 4. extract runtime dependencies for each package into $_rundeps_$varpkg
+# 5. extract a [sorted] list of categories into $PACKAGE_CATEGORIES
+# 6. create $_npkgs_$varcat holding the total number of packages in category
+#
+# NOTE: $varpkg is the product of f_str2varname $package varpkg
+# NOTE: $package is the name as it appears in the INDEX (no archive suffix)
+# NOTE: We only show categories for which there are at least one package.
+# NOTE: $varcat is the product of f_str2varname $category varcat
+#
+f_index_read()
+{
+ local var_to_get="${1:-PACKAGE_INDEX}"
+
+ # Export variables required by awk(1) below
+ export msg_no_description_provided
+ export msg_all msg_all_desc
+ export VALID_VARNAME_CHARS
+ export msg_packages
+
+ eval "$( debug= f_getvar "$var_to_get" | awk -F'|' '
+ function _asorti(src, dest)
+ {
+ k = nitems = 0
+
+ # Copy src indices to dest and calculate array length
+ for (i in src) dest[++nitems] = i
+
+ # Sort the array of indices (dest) using insertion sort method
+ for (i = 1; i <= nitems; k = i++)
+ {
+ idx = dest[i]
+ while ((k > 0) && (dest[k] > idx))
+ {
+ dest[k+1] = dest[k]
+ k--
+ }
+ dest[k+1] = idx
+ }
+
+ return nitems
+ }
+ function print_category(category, npkgs, desc)
+ {
+ cat = category
+ # Accent the category if the first page has been
+ # cached (also acting as a visitation indicator)
+ if ( ENVIRON["_index_page_" varcat "_1"] )
+ cat = cat "*"
+ printf "'\''%s'\'' '\''%s " packages "'\'' '\''%s'\''\n",
+ cat, npkgs, desc
+ }
+ BEGIN {
+ valid_chars = ENVIRON["VALID_VARNAME_CHARS"]
+ default_desc = ENVIRON["msg_no_description_provided"]
+ packages = ENVIRON["msg_packages"]
+ tpkgs = 0
+ prefix = ""
+ }
+ {
+ tpkgs++
+ varpkg = $1
+ gsub("[^" valid_chars "]", "_", varpkg)
+ print "_categories_" varpkg "=\"" $7 "\""
+ split($7, pkg_categories, /[[:space:]]+/)
+ for (pkg_category in pkg_categories)
+ categories[pkg_categories[pkg_category]]++
+ print "_rundeps_" varpkg "=\"" $9 "\""
+ }
+ END {
+ print "_npkgs=" tpkgs # For convenience, total package count
+
+ n = _asorti(categories, categories_sorted)
+
+ # Produce package counts for each category
+ for (i = 1; i <= n; i++)
+ {
+ cat = varcat = categories_sorted[i]
+ npkgs = categories[cat]
+ gsub("[^" valid_chars "]", "_", varcat)
+ print "_npkgs_" varcat "=\"" npkgs "\""
+ }
+
+ # Create menu list and generate list of categories at same time
+ print "CATEGORY_MENU_LIST=\""
+ print_category(ENVIRON["msg_all"], tpkgs,
+ ENVIRON["msg_all_desc"])
+ category_list = ""
+ for (i = 1; i <= n; i++)
+ {
+ cat = varcat = categories_sorted[i]
+ npkgs = categories[cat]
+ cur_prefix = tolower(substr(cat, 1, 1))
+ if ( prefix != cur_prefix )
+ prefix = cur_prefix
+ else
+ cat = " " cat
+ gsub("[^" valid_chars "]", "_", varcat)
+ desc = ENVIRON["_category_" varcat]
+ if ( ! desc ) desc = default_desc
+ print_category(cat, npkgs, desc)
+ category_list = category_list " " cat
+ }
+ print "\""
+
+ # Produce the list of categories (calculated in above block)
+ sub(/^ /, "", category_list)
+ print "PACKAGE_CATEGORIES=\"" category_list "\""
+
+ }' )" # End-Quote
+}
+
+# f_index_extract_pages $var_to_get $var_basename $pagesize [$category]
+#
+# Extracts the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is
+# NULL; but should not be missing) into a series of sequential variables
+# corresponding to "pages" containing up to $pagesize packages. The package
+# INDEX data must be contained in the variable $var_to_get. The extracted pages
+# are stored in variables ${var_basename}_# -- where "#" is a the page number.
+# If $category is set, only packages for that category are extracted.
+# Otherwise, if $category is "All", missing, or NULL, all packages are
+# extracted and no filtering is done.
+#
+f_index_extract_pages()
+{
+ local var_to_get="${1:-PACKAGE_INDEX}" var_basename="$2" pagesize="$3"
+ local category="$4" # Optional
+
+ eval "$(
+ debug= f_getvar "$var_to_get" | awk -F'|' \
+ -v cat="$category" \
+ -v pagesize="$pagesize" \
+ -v var_basename="$var_basename" \
+ -v i18n_all="$msg_all" '
+ BEGIN { n = page = 0 }
+ /'\''/{ gsub(/'\''/, "'\''\\'\'\''") }
+ {
+ if ( cat !~ "(^$|^" i18n_all "$)" && $7 !~ \
+ "(^|[[:space:]])" cat "([[:space:]]|$)" ) next
+ starting_new_page = (n++ == (pagesize * page))
+ if ( starting_new_page )
+ printf "%s%s", ( n > 1 ? "'\''\n" : "" ),
+ var_basename "_" ++page "='\''"
+ printf "%s%s", ( starting_new_page ? "" : "\n" ), $0
+ }
+ END { if ( n > 0 ) print "'\''" }'
+ )"
+}
+
+# f_index_search $var_to_get $name [$var_to_set]
+#
+# Search the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is
+# NULL; but should not be missing) for $name, returning the first match.
+# Matches are strict (not regular expressions) and must match the beginning
+# portion of the package name to be considered a match. If $var_to_set is
+# missing or NULL, output is sent to standard output. If a match is found,
+# returns success; otherwise failure.
+#
+f_index_search()
+{
+ local __var_to_get="${1:-PACKAGE_INDEX}" __pkg_basename="$2"
+ local __var_to_set="$3"
+
+ f_dprintf "f_index_search: Searching package data (in %s) for %s" \
+ "$__var_to_get" "$__pkg_basename"
+
+ local __pkg=
+ __pkg=$( debug= f_getvar "$__var_to_get" |
+ awk -F'|' -v basename="$__pkg_basename" '
+ BEGIN { n = length(basename) }
+ substr($1, 0, n) == basename { print $1; exit }
+ ' )
+ if [ ! "$__pkg" ]; then
+ f_dprintf "f_index_search: No packages matching %s found" \
+ "$__pkg_basename"
+ return $FAILURE
+ fi
+
+ f_dprintf "f_index_search: Found package %s" "$__pkg"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__pkg"
+ else
+ echo "$__pkg"
+ fi
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." packages/index.subr
+
+fi # ! $_PACKAGES_INDEX_SUBR
diff --git a/usr.sbin/bsdconfig/share/packages/musthavepkg.subr b/usr.sbin/bsdconfig/share/packages/musthavepkg.subr
new file mode 100644
index 0000000..929823e
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/musthavepkg.subr
@@ -0,0 +1,87 @@
+if [ ! "$_PACKAGES_MUSTHAVEPKG_SUBR" ]; then _PACKAGES_MUSTHAVEPKG_SUBR=1
+#
+# Copyright (c) 2014 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." packages/musthavepkg.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/mustberoot.subr
+
+############################################################ FUNCTIONS
+
+# f_musthavepkg_init
+#
+# Validate pkg(8) is installed and set $PKG_ABI global if not already set.
+# Returns success unless pkg(8) is not installed and user refuses to install
+# it (upon prompt when running interactively).
+#
+f_musthavepkg_init()
+{
+ local funcname=f_musthavepkg_init
+ local pkg_abi_awk='$1~/^ABI/{print $NF; exit}'
+
+ if [ "$PKG_ABI" ]; then # Already set
+ f_dprintf "PKG_ABI=[%s]" "$PKG_ABI"
+ export PKG_ABI
+ f_quietly pkg -N -vv # return status (pkg(8) functional?)
+ return $?
+ fi
+
+ # Attempt to get PKG_ABI without prematurely bootstrapping pkg(8)
+ if f_eval_catch -k PKG_ABI $funcname pkg \
+ "pkg -N -vv | awk '%s'" "$pkg_abi_awk"
+ then
+ f_dprintf "PKG_ABI=[%s]" "$PKG_ABI"
+ export PKG_ABI
+ return $SUCCESS
+ fi
+
+ # pkg(8) not yet bootstrapped; ask for permission unless nonInteractive
+ f_dialog_yesno "$msg_pkg_not_yet_installed_install_now" ||
+ f_die 1 "$msg_must_have_pkg_to_execute" "$pgm"
+
+ f_mustberoot_init # Have to be root to install pkg(8)
+
+ # Bootstrap pkg(8)
+ f_dialog_info "$msg_bootstrapping_pkg"
+ f_eval_catch -k PKG_ABI $funcname pkg \
+ "ASSUME_ALWAYS_YES=1 pkg -vv | awk '%s'" "$pkg_abi_awk" ||
+ f_die 1 "$msg_must_have_pkg_to_execute" "$pgm"
+
+ f_dprintf "PKG_ABI=[%s]" "$PKG_ABI"
+ export PKG_ABI
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." packages/musthavepkg.subr
+
+fi # ! $_PACKAGES_MUSTHAVEPKG_SUBR
diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr
new file mode 100644
index 0000000..f3ec707
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/packages/packages.subr
@@ -0,0 +1,1192 @@
+if [ ! "$_PACKAGES_PACKAGES_SUBR" ]; then _PACKAGES_PACKAGES_SUBR=1
+#
+# Copyright (c) 2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." "$0"
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/media/common.subr
+f_include $BSDCFG_SHARE/packages/categories.subr
+f_include $BSDCFG_SHARE/packages/index.subr
+f_include $BSDCFG_SHARE/packages/musthavepkg.subr
+f_include $BSDCFG_SHARE/strings.subr
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+f_include_lang $BSDCFG_LIBE/include/messages.subr
+
+############################################################ CONFIGURATION
+
+#
+# How many packages to display (maximum) per dialog menubox.
+#
+: ${PACKAGE_MENU_PAGESIZE:=2000}
+
+############################################################ GLOBALS
+
+#
+# Package extensions to try
+#
+PACKAGE_EXTENSIONS=".txz .tbz .tbz2 .tgz"
+
+#
+# Variables used to track runtime states
+#
+PACKAGES_DETECTED= # Boolean (NULL/non-NULL); detected installed packages?
+PACKAGE_CATEGORIES= # List of package categories parsed from INDEX
+SELECTED_PACKAGES= # Packages selected by user in [X]dialog(1) interface
+
+#
+# Options
+#
+[ "${SHOW_DESC+set}" ] || SHOW_DESC=1
+
+############################################################ FUNCTIONS
+
+# eval f_package_accent_category_menu $var_to_set $CATEGORY_MENU_LIST
+#
+# Accent the CATEGORY_MENU_LIST produced by f_index_read() (see
+# packages/index.subr). Accented information includes adding an asterisk to the
+# category name if its index has been cached, adding the number of installed
+# packages for each category, and adding the number _selected_ packages for
+# each category.
+#
+# NOTE: The reason `eval' is recommended/shown for the syntax above is because
+# the $CATEGORY_MENU_LIST generated by f_index_read() is meant to be expanded
+# prior to execution (it contains a series of pre-quoted strings which act as
+# the interpolated command arguments).
+#
+f_package_accent_category_menu()
+{
+ local var_to_set="$1" category cat desc help varcat menu_buf n
+ shift 1 # var_to_set
+ while [ $# -gt 0 ]; do
+ category="${1%\*}" desc="${2%%; *}" help="$3"
+ shift 3 # cat/desc/help
+
+ cat="${category# }" # Trim lead space inserted by sort-method
+ f_str2varname "$cat" varcat
+
+ # Add number of installed packages for this category (if any)
+ n=0
+ case "$cat" in
+ "$msg_all") debug= f_getvar "_All_ninstalled" n ;;
+ *) debug= f_getvar "_${varcat}_ninstalled" n ;;
+ esac &&
+ [ $n -ge 1 ] && desc="$desc; $n $msg_installed_lc"
+
+ # Add number of selected packages for this category (if any)
+ n=0
+ case "$cat" in
+ "$msg_all") debug= f_getvar "_All_nselected" n ;;
+ *) debug= f_getvar "_${varcat}_nselected" n ;;
+ esac &&
+ [ $n -ge 1 ] && desc="$desc; $n $msg_selected"
+
+ # Re-Add asterisk to the category if its index has been cached
+ f_isset _index_page_${varcat}_1 && category="$category*"
+
+ # Update buffer with modified elements
+ menu_buf="$menu_buf
+ '$category' '$desc' '$help'" # End-Quote
+ done
+ setvar "$var_to_set" "$menu_buf" # return our buffer
+}
+
+# f_package_select $package ...
+#
+# Add $package to the list of tracked/selected packages. If $package is already
+# being tracked (already apears in $SELECTED_PACKAGES), this function amounts
+# to having no effect.
+#
+f_package_select()
+{
+ local package pkgsel
+ while [ $# -gt 0 ]; do
+ package="$1"
+ shift 1 # package
+ for pkgsel in $SELECTED_PACKAGES; do
+ [ "$package" = "$pkgsel" ] && return $SUCCESS
+ done
+ SELECTED_PACKAGES="$SELECTED_PACKAGES $package"
+ f_dprintf "Added %s to selection list" "$package"
+ done
+ SELECTED_PACKAGES="${SELECTED_PACKAGES# }" # Trim leading space
+}
+
+# f_package_deselect $package ...
+#
+# Remove $package from teh list of tracked/selected packages. If $package is
+# not being tracked (doesn't appear in $SELECTED_PACKAGES), this function
+# amounts to having no effet.
+#
+f_package_deselect()
+{
+ local package pkgsel
+ while [ $# -gt 1 ]; do
+ local new_list=""
+ package="$1"
+ shift 1 # package
+ for pkgsel in $SELECTED_PACKAGES; do
+ [ "$pkgsel" = "$package" ] && continue
+ new_list="$new_list${new_list:+ }$pkgsel"
+ done
+ SELECTED_PACKAGES="$new_list"
+ f_dprintf "Removed %s from selection list" "$package"
+ done
+}
+
+# f_package_detect_installed
+#
+# Detect installed packages. Currently this uses pkg-query(8) for querying
+# entries and marks each entry as an installed/selected package.
+#
+f_package_detect_installed()
+{
+ local package varpkg
+ for package in $( pkg query "%n-%v" ); do
+ f_str2varname $package varpkg
+ export _mark_$varpkg=X # exported for awk(1) ENVIRON[]
+ f_package_select $package
+ done
+}
+
+# f_package_calculate_totals
+#
+# Calculate number of installed/selected packages for each category listed in
+# $PACKAGE_CATEGORIES (the number of installed packages for $category is stored
+# as $_${varcat}_ninstalled -- where $varcat is the product of `f_str2varname
+# $category varcat' -- and number selected packages as $_${varcat}_nselected).
+# Also calculates the total number of installed/selected packages stored as
+# $_All_ninstalled and $_All_nselected.
+#
+# Calculations are peformed by checking "marks". A "mark" is stored as
+# $_mark_$varpkg -- where $varpkg is the product of `f_str2varname $package
+# varpkg'. A mark can be "X" for an installed package, `I' for a package that
+# is marked for installation, "R" for a package that is marked for re-install,
+# and "U" for a package that is marked for uninstallation. If a package mark is
+# NULL or a single space (e.g., " "), the package is considered to be NOT
+# selected (and therefore does not increment the counts calculated herein).
+#
+f_package_calculate_totals()
+{
+ local pkg varpkg mark cat varcat pkgcat n tselected=0 tinstalled=0
+ for cat in $PACKAGE_CATEGORIES; do
+ f_str2varname $cat varcat
+ setvar _${varcat}_ninstalled=0
+ setvar _${varcat}_nselected=0
+ done
+ for pkg in $SELECTED_PACKAGES; do
+ f_str2varname $pkg varpkg
+ mark=
+ f_getvar _mark_$varpkg mark
+ case "$mark" in
+ ""|" ") : ;;
+ X) tinstalled=$(( $tinstalled + 1 )) ;;
+ *) tselected=$(( $tselected + 1 ))
+ esac
+ f_getvar _categories_$varpkg pkgcat
+ for cat in $pkgcat; do
+ f_str2varname $cat varcat
+ case "$mark" in
+ ""|" ") : ;;
+ X) debug= f_getvar _${varcat}_ninstalled n
+ setvar _${varcat}_ninstalled $(( $n + 1 )) ;;
+ *) debug= f_getvar _${varcat}_nselected n
+ setvar _${varcat}_nselected $(( $n + 1 ))
+ esac
+ done
+ done
+ _All_nselected=$tselected
+ _All_ninstalled=$tinstalled
+}
+
+# f_package_calculate_rundeps
+#
+# Update package dependencies by first unmarking all dependencies and then
+# re-marking all dependencies of packages marked for either install ("I") or
+# re-install ("R").
+#
+f_package_calculate_rundeps()
+{
+ local pkg varpkg mark rundeps dep vardep
+
+ #
+ # First unmark all the existing run-dependencies
+ #
+ f_dprintf "Unselecting package run-dependencies..."
+ for pkg in $SELECTED_PACKAGES; do
+ f_str2varname $pkg varpkg
+ mark=
+ debug= f_getvar _mark_$varpkg mark
+ # Only unmark if it's marked as a Dependency
+ if [ "$mark" = "D" ]; then
+ f_dprintf "%s unselected" $pkg
+ unset _mark_$varpkg
+ f_package_deselect $pkg
+ fi
+ done
+
+ #
+ # Processes selected packages, adding dependencies
+ #
+ f_dprintf "Re-selecting package run-dependencies..."
+ for pkg in $SELECTED_PACKAGES; do
+ f_str2varname $pkg varpkg
+ mark=
+ debug= f_getvar _mark_$varpkg mark
+ # Skip pkg unless marked for [Re-]Install
+ [ "$mark" = "I" -o "$mark" = "R" ] || continue
+ f_getvar _rundeps_$varpkg rundeps
+ for dep in $rundeps; do
+ f_str2varname $dep vardep
+ mark=
+ debug= f_getvar _mark_$vardep mark
+ # Skip dep if already marked
+ [ "${mark:- }" = " " ] || continue
+ export _mark_$vardep="D"
+ f_package_select $dep
+ done
+ done
+
+ f_dprintf "Finished recalculating dependencies."
+}
+
+# f_package_menu_categories $var_to_set $defaultitem
+#
+# Dislay the menu of package categories, complete with package counts for each
+# category, accents, and other miscellany. If $defaultitem is non-NULL and
+# matches one of the existing menu-items, it will be pre-highlighted in the
+# menu dialog (HINT: Use f_dialog_menutag_fetch() to populate a local variable
+# that is passed as $defaultitem to highlight the user's last selection).
+#
+f_package_menu_categories()
+{
+ local var_to_get="$1" defaultitem="$2"
+ local prompt="$msg_please_select_a_category_to_display"
+ local menu_list="
+ '> $msg_review' '$msg_review_desc' '$msg_review_help'
+ " # End-Quote
+ local hline=
+
+ f_package_calculate_rundeps
+ # updates package mark variables and SELECTED_PACKAGES
+ f_package_calculate_totals
+ # creates _{varcat}_ninstalled and _{varcat}_nselected
+
+ local category_list
+ debug= f_getvar "$var_to_get" category_list || return $DIALOG_CANCEL
+
+ # Accent the category menu list with ninstalled/nselected
+ eval f_package_accent_category_menu category_list $category_list
+
+ # Add list of categories to menu list
+ menu_list="$menu_list $category_list"
+
+ local height width rows
+ eval f_dialog_menu_with_help_size height width rows \
+ \"\$DIALOG_TITLE\" \
+ \"\$DIALOG_BACKTITLE\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+ local menu_choice
+ menu_choice=$( eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --item-help \
+ --default-item \"\$defaultitem\" \
+ --ok-label \"$msg_select\" \
+ --cancel-label \"$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_menutag_store -s "$menu_choice"
+ return $retval
+}
+
+# f_package_index_get_page $category $page [$var_to_set [$var_to_get]]
+#
+# Obtain a [potentially cached] page of the INDEX file for a given $category.
+# If $page is 1 and the cache has not yet been generated, the cache-generating
+# function f_index_extract_pages() (above) is called to generate all pages
+# (not just the requested page) in cache before returning the requested page.
+# If $page is not 1 and there is no cached page, failure status is returned.
+#
+f_package_index_get_page()
+{
+ local category="$1" page="$2" var_to_set="$3" var_to_get="$4" varcat
+ f_str2varname "$category" varcat
+ if ! debug= f_getvar "_index_page_${varcat}_$page" $var_to_set &&
+ [ "$page" = "1" ]
+ then
+ f_show_info "$msg_building_package_menus"
+ local pagesize="$PACKAGE_MENU_PAGESIZE"
+ f_index_extract_pages "${var_to_get:-PACKAGE_INDEX}" \
+ _index_page_${varcat} "$pagesize" "$category"
+ debug= f_getvar _index_page_${varcat}_$page $var_to_set
+
+ # Update category default-item because now we're cached
+ [ $page -eq 1 ] &&
+ category_defaultitem="${category_defaultitem%\*}*"
+ else
+ return $FAILURE
+ fi
+}
+
+# f_package_menu_select $category [$page [$defaultitem]]
+#
+# Display list of packages for $category, optionally $page N and with a default
+# item selected. If $page is omitted, the first page is displayed (but this
+# only matters if there are multiple pages; which is determined by the global
+# maximum $PACKAGE_MENU_PAGESIZE).
+#
+# On success, if the user doesn't press ESC or choose Cancel, use
+# f_dialog_menuitem_fetch() to populate a local variable with the item (not
+# tag) corresponding to the user's selection. The tag portion of the user's
+# selection is available through f_dialog_menutag_fetch().
+#
+f_package_menu_select()
+{
+ local category="$1" page="${2:-1}"
+ local prompt= # Calculated below
+ local menu_list # Calculated below
+ local defaultitem="$3"
+ local hline="$hline_arrows_tab_punc_enter"
+
+ f_isinteger "$page" || return $DIALOG_CANCEL
+
+ local varcat
+ f_str2varname "$category" varcat
+
+ # Get number of packages for this category
+ local npkgs=0
+ case "$category" in
+ "$msg_all"|"") npkgs="${_npkgs:-0}" ;;
+ *) f_getvar _npkgs_$varcat npkgs
+ esac
+
+ # Calculate number of pages
+ local npages=$(( ${npkgs:=0} / $PACKAGE_MENU_PAGESIZE ))
+
+ # Add a page to the pagecount if not evenly divisible
+ [ $(( $npages * $PACKAGE_MENU_PAGESIZE )) -lt $npkgs ] &&
+ npages=$(( $npages + 1 ))
+
+ # Print some debugging information
+ f_dprintf "f_package_menu_select: category=[%s] npkgs=%u npages=%u" \
+ "$category" "$npkgs" "$npages"
+
+ local add_prev="" add_next=""
+ local previous_page="$msg_previous_page" next_page="$msg_next_page"
+ if [ $page -gt 1 ]; then
+ add_prev=1
+ # Accent the `Previous Page' item with an asterisk
+ # if the page-before-previous is loaded/cached
+ f_isset _index_page_${varcat}_$(( $page - 1 )) &&
+ previous_page="$previous_page*"
+ fi
+ if [ $page -lt $npages ]; then
+ add_next=1
+ # Accent the `Next Page' item with an asterisk
+ # if the page-after-next is loaded/cached
+ f_isset _index_page_${varcat}_$(( $page + 1 )) &&
+ next_page="$next_page*"
+ fi
+
+ local index_page
+ f_package_index_get_page "$category" $page index_page
+
+ menu_list="
+ ${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}}
+ ${add_next:+'> $next_page' '' ${SHOW_DESC:+''}}
+ $(
+ export SHOW_DESC
+ export VALID_VARNAME_CHARS
+ echo "$index_page" | awk -F'|' -v view="port" '
+ BEGIN {
+ valid_chars = ENVIRON["VALID_VARNAME_CHARS"]
+ prefix = ""
+ }
+ {
+ cur_prefix = tolower(substr($1, 1, 1))
+ printf "'\''"
+ if ( prefix != cur_prefix )
+ prefix = cur_prefix
+ else
+ printf " "
+ package = $1
+ if ( view == "port" )
+ desc = $2
+ varpkg = package
+ gsub("[^" valid_chars "]", "_", varpkg)
+ mark = ENVIRON["_mark_" varpkg]
+ if ( ! mark ) mark = " "
+ printf "%s'\'' '\''[%c] %s'\''",
+ package, mark, desc
+ if ( ENVIRON["SHOW_DESC"] ) {
+ help = $4
+ gsub(/'\''/, "'\''\\'\'\''", help)
+ printf " '\''%s'\''", help
+ }
+ printf "\n"
+ }'
+ )
+ ${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}}
+ ${add_next:+'> $next_page' '' ${SHOW_DESC:+''}}
+ " # End-Quote
+
+ # Accept/Translate i18n "All" but other category names must
+ # match tree definitions from INDEX, ports, FTP, etc.
+ case "$category" in
+ "$msg_all"|"") f_category_desc_get "All" prompt ;;
+ *) f_category_desc_get "$category" prompt ;;
+ esac
+ f_sprintf prompt "%s $msg_page_of_npages" "$prompt" "$page" "$npages"
+
+ local mheight mwidth mrows
+ eval f_dialog_menu${SHOW_DESC:+_with_help}_size mheight mwidth mrows \
+ \"\$DIALOG_TITLE\" \"\$DIALOG_BACKTITLE\" \
+ \"\$prompt\" \"\$hline\" $menu_list
+ local iheight iwidth
+ f_dialog_infobox_size iheight iwidth \
+ "$DIALOG_TITLE" "$DIALOG_BACKTITLE" \
+ "$msg_processing_selection"
+
+ local menu_choice
+ menu_choice=$( eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --keep-tite \
+ --ok-label \"$msg_select\" \
+ --cancel-label \"$msg_back\" \
+ ${SHOW_DESC:+--item-help} \
+ --default-item \"\$defaultitem\" \
+ --menu \"\$prompt\" \
+ $mheight $mwidth $mrows \
+ $menu_list \
+ --and-widget \
+ ${USE_XDIALOG:+--no-buttons} \
+ --infobox \"\$msg_processing_selection\" \
+ $iheight $iwidth \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_data_sanitize menu_choice
+ f_dialog_menutag_store "$menu_choice"
+
+ if [ $retval -eq $DIALOG_OK ]; then
+ local item
+ item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \
+ \"\$menu_choice\" $menu_list )
+ f_dialog_menuitem_store "$item"
+ fi
+
+ return $retval
+}
+
+# f_package_menu_deselect $package
+#
+# Display a menu, asking the user what they would like to do with $package
+# with regard to "deselecting" an already installed package. Choices include
+# uninstall, re-install, or cancel (leave $package marked as installed).
+# Returns success if the user does not press ESC or choose Cnacel. Use the
+# f_dialog_menutag_fetch() function upon success to retrieve the user's choice.
+#
+f_package_menu_deselect()
+{
+ local package="$1"
+ local prompt # Calculated below
+ local menu_list="
+ 'X $msg_installed' '$msg_installed_desc'
+ 'R $msg_reinstall' '$msg_reinstall_desc'
+ 'U $msg_uninstall' '$msg_uninstall_desc'
+ " # End-Quote
+ local hline="$hline_alnum_arrows_punc_tab_enter"
+
+ f_sprintf prompt "$msg_what_would_you_like_to_do_with" "$package"
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$DIALOG_TITLE\" \
+ \"\$DIALOG_BACKTITLE\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+ local menu_choice
+ menu_choice=$( eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --ok-label \"$msg_select\" \
+ --cancel-label \"$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
+ )
+ local retval=$?
+ f_dialog_menutag_store -s "$menu_choice"
+ return $retval
+}
+
+# f_package_review
+#
+# Display a review screen, showing selected packages and what they are marked
+# for, before proceeding (if the user does not press ESC or choose Cancel) to
+# operate on each selection. Returns error if no packages have been selected,
+# or the user has pressed ESC, or if they have chosen Cancel.
+#
+f_package_review()
+{
+ local funcname=f_package_review
+ local prompt # Calculated below
+ local menu_list # Calculated below
+ local hline="$hline_alnum_arrows_punc_tab_enter"
+
+ f_dprintf "$funcname: SELECTED_PACKAGES=[%s]" "$SELECTED_PACKAGES"
+
+ f_sprintf prompt "$msg_reviewing_selected_packages" "$_All_nselected"
+
+ local package varpkg mark
+ for package in $SELECTED_PACKAGES; do
+ mark=
+ f_str2varname "$package" varpkg
+ f_getvar _mark_$varpkg mark
+ [ "$mark" -a ! "${mark#[IRUD]}" ] || continue
+ menu_list="$menu_list
+ '$mark' '$package'
+ " # End-Quote
+ done
+ if [ ! "$menu_list" ]; then
+ f_show_msg "$msg_no_packages_were_selected_for_extraction"
+ return $DIALOG_CANCEL # Might have selected this by accident
+ fi
+ menu_list=$( echo "$menu_list" | sort )
+
+ local height width rows
+ eval f_dialog_menu_size height width rows \
+ \"\$DIALOG_TITLE\" \
+ \"\$DIALOG_BACKTITLE\" \
+ \"\$prompt\" \
+ \"\$hline\" \
+ $menu_list
+
+ # Show the review menu (ignore menu choice)
+ eval $DIALOG \
+ --title \"\$DIALOG_TITLE\" \
+ --backtitle \"\$DIALOG_BACKTITLE\" \
+ --hline \"\$hline\" \
+ --ok-label \"\$msg_proceed\" \
+ --cancel-label \"\$msg_cancel\" \
+ --menu \"\$prompt\" \
+ $height $width $rows \
+ $menu_list \
+ 2> /dev/null || return $?
+ # Return if the user pressed ESC or chose Cancel/No
+
+ #
+ # Process each of the selected packages:
+ # + First, process packages marked for Install.
+ # + Second, process packages marked for Re-install.
+ # + Finally, process packages marked for Uninstall.
+ #
+ for package in $SELECTED_PACKAGES; do
+ mark=
+ f_str2varname "$package" varpkg
+ debug= f_getvar _mark_$varpkg mark
+ [ "$mark" = "I" ] || continue
+ f_dprintf "$funcname: Installing %s package" "$package"
+ f_package_add "$package"
+ done
+ for package in $SELECTED_PACKAGES; do
+ mark=
+ f_str2varname "$package" varpkg
+ debug= f_getvar _mark_$varpkg mark
+ [ "$mark" = "R" ] || continue
+ f_dprintf "$funcname: Reinstalling %s package" "$package"
+ f_package_reinstall "$package"
+ done
+ for package in $SELECTED_PACKAGES; do
+ mark=
+ f_str2varname "$package" varpkg
+ debug= f_getvar _mark_$varpkg mark
+ [ "$mark" = "U" ] || continue
+ f_dprintf "$funcname: Uninstalling %s package" "$package"
+ f_package_delete "$package" || continue
+ f_package_deselect "$package"
+ done
+
+ return $DIALOG_OK
+}
+
+# f_package_config
+#
+# Allow the user to configure packages and install them. Initially, a list of
+# package categories is loaded/displayed. When the user selects a category,
+# the menus for that category are built (unlike sysinstall which built all
+# category menus up-front -- which also took forever, despite the fact that
+# few people visit more than a couple of categories each time).
+#
+f_package_config()
+{
+ # Did we get an INDEX?
+ f_index_initialize || return $FAILURE
+ # Creates following variables (indirectly via f_index_read())
+ # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg}
+ # PACKAGE_CATEGORIES _npkgs
+
+ # Detect installed packages (updates marks/SELECTED_PACKAGES)
+ f_package_detect_installed
+ export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[]
+
+ local retval category varcat defaultitem category_defaultitem=""
+ while :; do
+ # Display the list of package categories
+ f_package_menu_categories \
+ CATEGORY_MENU_LIST "$category_defaultitem"
+ retval=$?
+ f_dialog_menutag_fetch category
+ f_dprintf "retval=%u mtag=[%s]" $retval "$category"
+ category_defaultitem="$category"
+
+ [ $retval -eq $DIALOG_OK ] || break
+
+ # Maybe the user chose an action (like `Review')
+ case "$category" in
+ "> $msg_review")
+ f_package_review && break
+ continue ;;
+ "> "*)
+ continue
+ esac
+
+ # Anything else is a package category
+
+ category=${category# } # Trim leading space if present
+ category=${category%\*} # Trim trailing asterisk if present
+
+ f_str2varname "$category" varcat
+
+ local page package varpkg mark menu_choice
+ while :; do
+ # Display the list of packages for selected category
+ page=1 defaultitem=""
+ f_getvar _defaultitem_$varcat defaultitem
+ f_getvar _defaultpage_$varcat page
+ f_package_menu_select \
+ "$category" "${page:=1}" "$defaultitem"
+ retval=$?
+ f_dialog_menutag_fetch menu_choice
+ f_dprintf "retval=%u mtag=[%s]" $retval "$menu_choice"
+
+ # NOTE: When --and-widget is used only ESC will cause
+ # dialog(1) to return without going to the next widget.
+ # This is alright in our case as we can still detect
+ # the Cancel button because stdout will be NULL.
+ # Alternatively, Xdialog(1) will terminate with 1
+ # if/when Cancel is chosen on any widget.
+ if [ $retval -eq $DIALOG_ESC -o ! "$menu_choice" ]
+ then
+ break
+ elif [ $retval -eq $DIALOG_CANCEL ]; then
+ # Using X11, Xdialog(1) returned 1 for Cancel
+ f_show_msg "%s" "$menu_choice"
+ break
+ elif [ $retval -ne $DIALOG_OK ]; then
+ # X11-related error occurred using Xdialog(1)
+ f_show_msg "%s" "$menu_choice"
+ break
+ fi
+
+ defaultitem="$menu_choice"
+
+ # NOTE: f_package_menu_select() does not show the
+ # `Previous Page' or `Next Page' items unless needed
+ case "$menu_choice" in
+ "> $msg_previous_page"|"> $msg_previous_page*")
+ page=$(( $page - 1 ))
+ setvar _defaultpage_$varcat $page
+ # Update default-item to match accent that will
+ # be applied by f_package_menu_select(); if the
+ # page-before-prev is cached, add an asterisk.
+ if f_isset \
+ _index_page_${varcat}_$(( $page - 1 ))
+ then
+ defaultitem="${defaultitem%\*}*"
+ else
+ defaultitem="${defaultitem%\*}"
+ fi
+ setvar _defaultitem_$varcat "$defaultitem"
+ continue ;;
+ "> $msg_next_page"|"> $msg_next_page*")
+ page=$(( $page + 1 ))
+ setvar _defaultpage_$varcat $page
+ # Update default-item to match accent that will
+ # be applied by f_package_menu_select(); if the
+ # page-after-next is cached, add an asterisk.
+ if f_isset \
+ _index_page_${varcat}_$(( $page + 1 ))
+ then
+ defaultitem="${defaultitem%\*}*"
+ else
+ defaultitem="${defaultitem%\*}"
+ fi
+ setvar _defaultitem_$varcat "$defaultitem"
+ continue ;;
+ "> "*) # Unknown navigation/action item
+ setvar _defaultpage_$varcat $page
+ continue ;; # Do not treat as a package
+ *)
+ setvar _defaultitem_$varcat "$defaultitem"
+ esac
+
+ # Treat any other selection as a package
+ package="${menu_choice# }" # Trim leading space
+ f_str2varname $package varpkg
+ f_dialog_menuitem_fetch mark
+ mark="${mark#?}"
+ mark="${mark%%\] *}"
+ case "$mark" in
+ "I")
+ mark=" "
+ f_package_deselect $package
+ ;;
+ " "|"D")
+ mark="I"
+ f_package_select $package
+ ;;
+ "X"|"R"|"U")
+ f_package_menu_deselect $package || continue
+ f_dialog_menutag_fetch menu_choice
+ case "$menu_choice" in
+ "X $msg_installed")
+ f_package_deselect "$package"
+ mark="X"
+ ;;
+ "R $msg_reinstall")
+ f_package_select "$package"
+ mark="R"
+ ;;
+ "U $msg_uninstall")
+ f_package_select "$package"
+ mark="U"
+ ;;
+ esac
+ ;;
+ esac
+ export _mark_$varpkg="$mark"
+ # NOTE: exported for awk(1) ENVIRON[]
+ done
+ done
+}
+
+# f_package_add $package_name [$depended]
+#
+# Like f_package_extract(), but assumes current media device and chases deps.
+# Note that $package_name should not contain the archive suffix (e.g., `.tbz').
+# If $depended is present and non-NULL, the package is treated as a dependency
+# (in this function, dependencies are not handled any differently, but the
+# f_package_extract() function is passed this value and it displays a different
+# message when installing a dependency versus non-dependency).
+#
+f_package_add()
+{
+ local name="$1" depended="$2" status=$SUCCESS retval
+
+ local alert=f_show_msg no_confirm=
+ f_getvar $VAR_NO_CONFIRM no_confirm
+ [ "$no_confirm" ] && alert=f_show_info
+
+ if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; }
+ then
+ f_dprintf "packageAdd: %s" \
+ "$msg_no_package_name_passed_in_package_variable"
+ return $FAILURE
+ fi
+
+ { # Verify and initialize device media if-defined
+ f_media_verify &&
+ f_device_init device_media &&
+ f_index_initialize
+ } || return $FAILURE
+
+ # Now we have (indirectly via f_index_read()):
+ # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg}
+ # PACKAGE_CATEGORIES _npkgs
+
+ local varpkg
+ f_str2varname "$name" varpkg
+
+ # Just as-in the user-interface (opposed to scripted-use), only allow
+ # packages with at least one category to be recognized.
+ #
+ local pkgcat=
+ if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then
+ # $pkg may be a partial name, search the index (this is slow)
+ f_index_search PACKAGE_INDEX $name name
+ if [ ! "$name" ]; then
+ f_show_msg \
+ "$msg_sorry_package_was_not_found_in_the_index" \
+ "$name"
+ return $FAILURE
+ fi
+ f_str2varname "$name" varpkg
+ fi
+
+ # If invoked through the scripted interface, we likely have not yet
+ # detected the installed packages -- something we should do only once.
+ #
+ if [ ! "$PACKAGES_DETECTED" ]; then
+ f_dprintf "f_package_add: Detecting installed packages"
+ f_package_detect_installed
+ export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[]
+ fi
+ # Now we have: _mark_{varpkg}=X for all installed packages
+
+ #
+ # Since we're maintaining data structures for installed packages,
+ # short-circuit the package dependency checks if the package is already
+ # installed. This prevents wasted cycles, minor delays between package
+ # extractions, and worst-case an infinite loop with a certain faulty
+ # INDEX file.
+ #
+ local mark=
+ f_getvar _mark_$varpkg mark && [ "$mark" = "X" ] && return $SUCCESS
+
+ local dep vardep rundeps=
+ f_getvar _rundeps_$varpkg rundeps
+ for dep in $rundeps; do
+ f_str2varname "$dep" vardep
+
+ # Skip dependency if already installed
+ mark=
+ f_getvar _mark_$vardep mark && [ "$mark" = "X" ] && continue
+
+ # Just as-in the user-interface (opposed to scripted-use), only
+ # allow packages with at least one category to be recognized.
+ #
+ local depcat=
+ if ! f_getvar _categories_$vardep depcat || [ ! "$depcat" ]
+ then
+ $alert "$msg_required_package_not_found" "$dep"
+ [ "$no_confirm" ] && sleep 2
+ fi
+
+ f_package_add "$dep"
+ retval=$?
+ if [ $retval -ne $SUCCESS ]; then
+ status=$(( $status | $retval ))
+
+ # XXX package could be on a future disc volume
+ # XXX (not supporting multiple disc volumes yet)
+
+ $alert "$msg_loading_of_dependent_package_failed" \
+ "$dep"
+ [ "$no_confirm" ] && sleep 2
+ fi
+ done
+ [ $status -eq $SUCCESS ] || return $status
+
+ #
+ # Done with the deps? Try to load the real m'coy.
+ #
+
+ f_package_extract device_media "$name" "$depended"
+ retval=$?
+ if [ $retval -ne $SUCCESS ]; then
+ status=$(( $status | $retval ))
+ else
+ setvar _mark_$varpkg X
+ fi
+
+ return $status
+}
+
+# f_package_extract $device $name [$depended]
+#
+# Extract a package based on a namespec and media device. If $depended is
+# present and non-NULL, the notification displayed while installing the package
+# has "as a dependency" appended.
+#
+f_package_extract()
+{
+ local funcname=f_package_extract
+ local device="$1" name="$2" depended="$3"
+ local devname=
+
+ f_musthavepkg_init # Make sure we have a usable pkg(8) with $PKG_ABI
+
+ $device get name devname
+ f_dprintf "$funcname: device=[%s] name=[%s] depended=[%s]" \
+ "$devname" "$name" "$depended"
+
+ # Check to make sure it's not already there
+ local varpkg mark=
+ f_str2varname "$name" varpkg
+ f_getvar _mark_$varpkg mark
+ [ "$mark" = "X" ] && return $SUCCESS
+
+ if ! f_device_init $device; then
+ f_show_msg \
+ "$msg_unable_to_initialize_media_type_for_package_extract"
+ return $FAILURE
+ fi
+
+ # If necessary, initialize the ldconfig hints
+ [ -f "/var/run/ld-elf.so.hints" ] ||
+ f_quietly ldconfig /usr/lib /usr/lib/compat /usr/local/lib
+
+ # Make a couple paranoid locations for temp
+ # files to live if user specified none
+ local tmpdir
+ f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir
+ f_quietly mkdir -p -m 1777 "$tmpdir"
+
+ local path device_type
+ $device get type device_type
+ case "$name" in
+ */*) path="$name" ;;
+ *)
+ if [ "$device_type" = "$DEVICE_TYPE_HTTP" ]; then
+ path="$PKG_ABI/latest/All/$name"
+ else
+ path="packages/$PKG_ABI/All/$name"
+ fi
+ esac
+
+ # We have a path, call the device strategy routine to check the file
+ local pkg_ext found=
+ for pkg_ext in "" $PACKAGE_EXTENSIONS; do
+ if f_device_get $device "$path$pkg_ext" $PROBE_EXIST; then
+ path="$path$pkg_ext"
+ found=1
+ break
+ elif [ "$device_type" = "$DEVICE_TYPE_HTTP" ] &&
+ f_device_get $device \
+ "packages/$PKG_ABI/All/$name$pkg_ext" $PROBE_EXIST
+ then
+ # Mirroring physical media over HTTP
+ path="packages/$PKG_ABI/All/$name$pkg_ext"
+ found=1
+ break
+ fi
+ done
+ [ "$found" ] && f_dprintf "$funcname: found path=[%s] dev=[%s]" \
+ "$path" "$devname"
+
+ local alert=f_show_msg no_confirm=
+ f_getvar $VAR_NO_CONFIRM no_confirm
+ [ "$no_confirm" ] && alert=f_show_info
+
+ if [ ! "$found" ]; then
+ f_dprintf "$funcname: No such %s file on %s device" \
+ "$path" "$devname"
+ $alert "$msg_unable_to_fetch_package_from_selected_media" \
+ "$name"
+ [ "$no_confirm" ] && sleep 2
+ return $FAILURE
+ fi
+
+ if [ "$depended" ]; then
+ f_show_info "$msg_adding_package_as_a_dependency_from_media" \
+ "$name" "$devname"
+ else
+ f_show_info "$msg_adding_package_from_media" "$name" "$devname"
+ fi
+
+ # Request the package be added via pkg-install(8)
+ if f_debugging; then
+ f_eval_catch $funcname pkg \
+ 'pkg -d install -${depended:+A}y "%s"' "$name"
+ else
+ f_eval_catch $funcname pkg \
+ 'pkg install -${depended:+A}y "%s"' "$name"
+ fi
+ if [ $? -ne $SUCCESS ]; then
+ $alert "$msg_pkg_install_apparently_did_not_like_the_package" \
+ "$name"
+ [ "$no_confirm" ] && sleep 2
+ else
+ f_show_info "$msg_package_was_added_successfully" "$name"
+ sleep 1
+ fi
+
+ return $SUCCESS
+}
+
+# f_package_delete $name
+#
+# Delete package by full $name (lacks archive suffix; e.g., `.tbz').
+#
+f_package_delete()
+{
+ local funcname=f_package_delete
+ local name="$1"
+
+ if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; }
+ then
+ f_dprintf "packageDelete: %s" \
+ "$msg_no_package_name_passed_in_package_variable"
+ return $FAILURE
+ fi
+
+ f_dprintf "$funcname: name=[%s]" "$name"
+
+ [ "$name" ] || return $FAILURE
+
+ { # Verify and initialize device media if-defined
+ f_media_verify &&
+ f_device_init device_media &&
+ f_index_initialize
+ } || return $FAILURE
+
+ # Now we have (indirectly via f_index_read()):
+ # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg}
+ # PACKAGE_CATEGORIES _npkgs
+
+ local varpkg
+ f_str2varname "$name" varpkg
+
+ # Just as-in the user-interface (opposed to scripted-use), only allow
+ # packages with at least one category to be recognized.
+ #
+ local pkgcat=
+ if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then
+ # $pkg may be a partial name, search the index (this is slow)
+ f_index_search PACKAGE_INDEX "$name" name
+ if [ ! "$name" ]; then
+ f_show_msg \
+ "$msg_sorry_package_was_not_found_in_the_index" \
+ "$name"
+ return $FAILURE
+ fi
+ f_str2varname "$name" varpkg
+ fi
+
+ # If invoked through the scripted interface, we likely have not yet
+ # detected the installed packages -- something we should do only once.
+ #
+ if [ ! "$PACKAGES_DETECTED" ]; then
+ f_dprintf "$funcname: Detecting installed packages"
+ f_package_detect_installed
+ export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[]
+ fi
+ # Now we have: _mark_{varpkg}=X for all installed packages
+
+ #
+ # Return failure if the package is not already installed.
+ #
+ local pkgmark=
+ f_getvar _mark_$varpkg pkgmark
+ if ! [ "$pkgmark" -a ! "${pkgmark#[XUR]}" ]; then
+ f_show_msg "$msg_package_not_installed_cannot_delete" "$name"
+ return $FAILURE
+ fi
+
+ #
+ # Check for dependencies
+ #
+ local pkgsel depc=0 udeps=
+ for pkgsel in $SELECTED_PACKAGES; do
+ local mark=
+ f_str2varname $pkgsel varpkg
+ debug= f_getvar _mark_$varpkg mark
+ [ "$mark" -a ! "${mark#[XUR]}" ] || continue
+ local dep rundeps=
+ debug= f_getvar _rundeps_$varpkg rundeps
+ for dep in $rundeps; do
+ if [ "$dep" = "$name" ]; then
+ # Maybe this package is marked for deletion too
+ if [ "$mark" = "U" ]; then
+ udeps="$udeps $pkgsel"
+ else
+ depc=$(( $depc + 1 ))
+ fi
+ break
+ fi
+ done
+ done
+ if [ $depc -gt 0 ]; then
+ local grammatical_s=
+ [ $depc -gt 1 ] && grammatical_s=s
+ f_show_msg \
+ "$msg_package_is_needed_by_other_installed_packages" \
+ "$name" "$depc" "$grammatical_s"
+ return $FAILURE
+ fi
+
+ #
+ # Chase dependencies that are marked for uninstallation
+ #
+ for pkgsel in $udeps; do
+ f_dprintf "$funcname: Uninstalling dependency %s (%s)" \
+ "$pkgsel" "marked for delete"
+ f_package_delete "$pkgsel"
+ done
+
+ #
+ # OK to perform the delete (no other packages depend on it)...
+ #
+ f_show_info "$msg_uninstalling_package_waiting_for_pkg_delete" "$name"
+ if f_debugging; then
+ f_eval_catch $funcname pkg 'pkg -d delete -y "%s"' "$name"
+ else
+ f_eval_catch $funcname pkg 'pkg delete -y "%s"' "$name"
+ fi
+ if [ $? -ne $SUCCESS ]; then
+ f_show_msg "$msg_pkg_delete_failed" "$name"
+ return $FAILURE
+ else
+ f_dprintf "$funcname: pkg-delete(8) of %s successful" "$name"
+ f_str2varname "$name" varpkg
+ setvar _mark_$varpkg ""
+ fi
+}
+
+# f_package_reinstall $name
+#
+# A simple wrapper to f_package_delete() + f_package_add()
+#
+f_package_reinstall()
+{
+ f_package_delete "$1" && f_package_add "$1"
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." packages/packages.subr
+
+fi # ! $_PACKAGES_PACKAGES_SUBR
diff --git a/usr.sbin/bsdconfig/share/script.subr b/usr.sbin/bsdconfig/share/script.subr
new file mode 100644
index 0000000..b562e99
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/script.subr
@@ -0,0 +1,219 @@
+if [ ! "$_SCRIPT_SUBR" ]; then _SCRIPT_SUBR=1
+#
+# Copyright (c) 2012-2014 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." script.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/media/any.subr
+f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/mustberoot.subr
+f_include $BSDCFG_SHARE/networking/services.subr
+f_include $BSDCFG_SHARE/packages/packages.subr
+f_include $BSDCFG_SHARE/usermgmt/group.subr
+f_include $BSDCFG_SHARE/usermgmt/user.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:
+#
+# + 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"
+ shift 2 # func resword
+ f_dprintf "f_dispatch: calling resword \`%s'" "$resword"
+ eval $func "$@"
+ local retval=$?
+ if [ $retval -ne $SUCCESS ]; then
+ local _ignore_this_error
+ f_getvar $VAR_NO_ERROR _ignore_this_error
+ [ "$_ignore_this_error" ] || f_die $retval \
+ "$msg_command_failed_rest_of_script_aborted" "$resword"
+ fi
+ 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 (global)
+# 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 funcname=f_script_load
+ local script="$1" config_file retval=$SUCCESS
+
+ f_dprintf "$funcname: 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
+ fi
+
+ local old_interactive=
+ f_getvar $VAR_NONINTERACTIVE old_interactive # save a copy
+
+ # Hint to others that we're running from a script, should they care
+ setvar $VAR_NONINTERACTIVE yes
+
+ if [ "$script" = "-" ]; then
+ f_dprintf "$funcname: Loading script from stdin"
+ eval "$( cat )"
+ retval=$?
+ else
+ f_dprintf "$funcname: Loading script \`%s'" "$script"
+ if [ ! -e "$script" ]; then
+ f_show_msg "$msg_unable_to_open" "$script"
+ return $FAILURE
+ fi
+ . "$script"
+ retval=$?
+ fi
+
+ [ "$old_interactive" ] &&
+ setvar $VAR_NONINTERACTIVE "$old_interactive"
+
+ return $retval
+}
+
+############################################################ MAIN
+
+#
+# Reserved words meant for scripting
+#
+
+# this file
+f_resword_new loadConfig f_script_load
+
+# device.subr
+f_resword_new deviceRescan f_device_rescan
+
+# media/common.subr
+f_resword_new mediaOpen f_media_open
+f_resword_new mediaClose f_media_close
+
+# media includes
+f_resword_new mediaGetType f_media_get_type # media/any.subr
+f_resword_new mediaSetCDROM f_media_set_cdrom # media/cdrom.subr
+f_resword_new mediaSetDOS f_media_set_dos # media/dos.subr
+f_resword_new mediaSetDirectory f_media_set_directory # media/directory.subr
+f_resword_new mediaSetFloppy f_media_set_floppy # media/floppy.subr
+f_resword_new mediaSetNFS f_media_set_nfs # media/nfs.subr
+f_resword_new mediaSetUFS f_media_set_ufs # media/ufs.subr
+f_resword_new mediaSetUSB f_media_set_usb # media/usb.subr
+f_resword_new optionsEditor f_media_options_menu # media/options.subr
+f_resword_new tcpMenuSelect f_dialog_menu_select_tcp # media/tcp.subr
+
+# media/ftp.subr
+f_resword_new mediaSetFTP f_media_set_ftp
+f_resword_new mediaSetFTPActive f_media_set_ftp_active
+f_resword_new mediaSetFTPPassive f_media_set_ftp_passive
+f_resword_new mediaSetFTPUserPass f_media_set_ftp_userpass
+
+# media/http.subr
+f_resword_new mediaSetHTTP f_media_set_http
+
+# media/httpproxy.subr
+f_resword_new mediaSetHTTPProxy f_media_set_http_proxy
+
+# networking/services.subr
+f_resword_new configPCNFSD f_config_pcnfsd
+
+# packages/packages.subr
+f_resword_new configPackages f_package_config
+f_resword_new packageAdd f_package_add
+f_resword_new packageDelete f_package_delete
+f_resword_new packageReinstall f_package_reinstall
+
+# usermgmt/group.subr
+f_resword_new addGroup f_group_add
+f_resword_new deleteGroup f_group_delete
+f_resword_new editGroup f_group_edit
+
+# usermgmt/user.subr
+f_resword_new addUser f_user_add
+f_resword_new deleteUser f_user_delete
+f_resword_new editUser f_user_edit
+
+# variable.subr
+f_resword_new installVarDefaults f_variable_set_defaults
+f_resword_new dumpVariables f_dump_variables
+
+f_dprintf "%s: Successfully loaded." script.subr
+
+fi # ! $_SCRIPT_SUBR
diff --git a/usr.sbin/bsdconfig/share/strings.subr b/usr.sbin/bsdconfig/share/strings.subr
new file mode 100644
index 0000000..487e061
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/strings.subr
@@ -0,0 +1,454 @@
+if [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
+#
+# Copyright (c) 2006-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+
+############################################################ GLOBALS
+
+#
+# A Literal newline (for use with f_replace_all(), or IFS, or whatever)
+#
+NL="
+" # END-QUOTE
+
+#
+# Valid characters that can appear in an sh(1) variable name
+#
+# Please note that the character ranges A-Z and a-z should be avoided because
+# these can include accent characters (which are not valid in a variable name).
+# For example, A-Z matches any character that sorts after A but before Z,
+# including A and Z. Although ASCII order would make more sense, that is not
+# how it works.
+#
+VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
+
+############################################################ FUNCTIONS
+
+# f_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_snprintf $var_to_set $size $format [$arguments ...]
+#
+# Similar to snprintf(3), write at most $size number of bytes into $var_to_set
+# using printf(1) syntax (`$format [$arguments ...]'). The value of $var_to_set
+# is NULL unless at-least one byte is stored from the output.
+#
+f_snprintf()
+{
+ local __var_to_set="$1" __size="$2"
+ shift 2 # var_to_set size
+ eval "$__var_to_set"=\$\( printf -- \"\$@\" \| \
+ awk -v max=\"\$__size\" \''
+ {
+ len = length($0)
+ max -= len
+ print substr($0,0,(max > 0 ? len : max + len))
+ if ( max < 0 ) exit
+ max--
+ }'\' \)
+}
+
+# f_sprintf $var_to_set $format [$arguments ...]
+#
+# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax
+# (`$format [$arguments ...]').
+#
+f_sprintf()
+{
+ local __var_to_set="$1"
+ shift 1 # var_to_set
+ eval "$__var_to_set"=\$\( printf -- \"\$@\" \)
+}
+
+# f_vsnprintf $var_to_set $size $format $format_args
+#
+# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
+# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
+# NULL unless at-least one byte is stored from the output.
+#
+# Example 1:
+#
+# limit=7 format="%s"
+# format_args="'abc 123'" # 3-spaces between abc and 123
+# f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1]
+#
+# Example 2:
+#
+# limit=12 format="%s %s"
+# format_args=" 'doghouse' 'foxhound' "
+# # even more spaces added to illustrate escape-method
+# f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
+#
+# Example 3:
+#
+# limit=13 format="%s %s"
+# f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
+# f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
+# format_args="'$arg1' '$arg2'" # use single-quotes to surround args
+# f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
+#
+# In all of the above examples, the call to f_vsnprintf() does not change. Only
+# the contents of $limit, $format, and $format_args changes in each example.
+#
+f_vsnprintf()
+{
+ eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
+}
+
+# f_vsprintf $var_to_set $format $format_args
+#
+# Similar to vsprintf(3), write a string into $var_to_set using printf(1)
+# syntax (`$format $format_args').
+#
+f_vsprintf()
+{
+ eval f_sprintf \"\$1\" \"\$2\" $3
+}
+
+# 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#-}"
+ [ "${arg:-x}" = "${arg%[!0-9]*}" ]
+}
+
+# f_uriencode [$text]
+#
+# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
+# characters are converted to `%XX' sequence where XX represents the hexa-
+# decimal ordinal of the non-alphanumeric character. If $text is missing, data
+# is instead read from standard input.
+#
+f_uriencode_awk='
+BEGIN {
+ output = ""
+ for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
+}
+{
+ sline = ""
+ slen = length($0)
+ for (n = 1; n <= slen; n++) {
+ char = substr($0, n, 1)
+ if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
+ sline = sline char
+ }
+ output = output ( output ? "%0a" : "" ) sline
+}
+END { print output }
+'
+f_uriencode()
+{
+ if [ $# -gt 0 ]; then
+ echo "$1" | awk "$f_uriencode_awk"
+ else
+ awk "$f_uriencode_awk"
+ fi
+}
+
+# f_uridecode [$text]
+#
+# Decode $text from a URI. Encoded characters are converted from their `%XX'
+# sequence into original unencoded ASCII sequences. If $text is missing, data
+# is instead read from standard input.
+#
+f_uridecode_awk='
+BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
+{
+ sline = ""
+ slen = length($0)
+ for (n = 1; n <= slen; n++)
+ {
+ seq = substr($0, n, 3)
+ if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
+ hex = substr(seq, 2, 2)
+ sline = sline chr[sprintf("%u", "0x"hex)]
+ n += 2
+ } else
+ sline = sline substr(seq, 1, 1)
+ }
+ print sline
+}
+'
+f_uridecode()
+{
+ if [ $# -gt 0 ]; then
+ echo "$1" | awk "$f_uridecode_awk"
+ else
+ awk "$f_uridecode_awk"
+ fi
+}
+
+# f_replaceall $string $find $replace [$var_to_set]
+#
+# Replace all occurrences of $find in $string with $replace. If $var_to_set is
+# either missing or NULL, the variable name is produced on standard out for
+# capturing in a sub-shell (which is less recommended due to performance
+# degradation).
+#
+# To replace newlines or a sequence containing the newline character, use $NL
+# as `\n' is not supported.
+#
+f_replaceall()
+{
+ local __left="" __right="$1"
+ local __find="$2" __replace="$3" __var_to_set="$4"
+ while :; do
+ case "$__right" in *$__find*)
+ __left="$__left${__right%%$__find*}$__replace"
+ __right="${__right#*$__find}"
+ continue
+ esac
+ break
+ done
+ __left="$__left${__right#*$__find}"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__left"
+ else
+ echo "$__left"
+ fi
+}
+
+# f_str2varname $string [$var_to_set]
+#
+# Convert a string into a suitable value to be used as a variable name
+# by converting unsuitable characters into the underscrore [_]. If $var_to_set
+# is either missing or NULL, the variable name is produced on standard out for
+# capturing in a sub-shell (which is less recommended due to performance
+# degradation).
+#
+f_str2varname()
+{
+ local __string="$1" __var_to_set="$2"
+ f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
+}
+
+# f_shell_escape $string [$var_to_set]
+#
+# Escape $string for shell eval statement(s) by replacing all single-quotes
+# with a special sequence that creates a compound string when interpolated
+# by eval with surrounding single-quotes.
+#
+# For example:
+#
+# foo="abc'123"
+# f_shell_escape "$foo" bar # bar=[abc'\''123]
+# eval echo \'$bar\' # produces abc'123
+#
+# This is helpful when processing an argument list that has to retain its
+# escaped structure for later evaluations.
+#
+# WARNING: Surrounding single-quotes are not added; this is the responsibility
+# of the code passing the escaped values to eval (which also aids readability).
+#
+f_shell_escape()
+{
+ local __string="$1" __var_to_set="$2"
+ f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
+}
+
+# f_shell_unescape $string [$var_to_set]
+#
+# The antithesis of f_shell_escape(), this function takes an escaped $string
+# and expands it.
+#
+# For example:
+#
+# foo="abc'123"
+# f_shell_escape "$foo" bar # bar=[abc'\''123]
+# f_shell_unescape "$bar" # produces abc'123
+#
+f_shell_unescape()
+{
+ local __string="$1" __var_to_set="$2"
+ f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
+}
+
+# f_expand_number $string [$var_to_set]
+#
+# Unformat $string into a number, optionally to be stored in $var_to_set. This
+# function follows the SI power of two convention.
+#
+# The prefixes are:
+#
+# Prefix Description Multiplier
+# k kilo 1024
+# M mega 1048576
+# G giga 1073741824
+# T tera 1099511627776
+# P peta 1125899906842624
+# E exa 1152921504606846976
+#
+# NOTE: Prefixes are case-insensitive.
+#
+# Upon successful completion, success status is returned; otherwise the number
+# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing)
+# on standard output. In the case of failure, the error status will be one of:
+#
+# Status Reason
+# 1 Given $string contains no digits
+# 2 An unrecognized prefix was given
+# 3 Result too large to calculate
+#
+f_expand_number()
+{
+ local __string="$1" __var_to_set="$2"
+ local __cp __num __bshift __maxinput
+
+ # Remove any leading non-digits
+ __string="${__string#${__string%%[0-9]*}}"
+
+ # Store the numbers (no trailing suffix)
+ __num="${__string%%[!0-9]*}"
+
+ # Produce `-1' if string didn't contain any digits
+ if [ ! "$__num" ]; then
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" -1
+ else
+ echo -1
+ fi
+ return 1 # 1 = "Given $string contains no digits"
+ fi
+
+ # Remove all the leading numbers from the string to get at the prefix
+ __string="${__string#"$__num"}"
+
+ #
+ # Test for invalid prefix (and determine bitshift length)
+ #
+ case "$__string" in
+ ""|[[:space:]]*) # Shortcut
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" $__num
+ else
+ echo $__num
+ fi
+ return $SUCCESS ;;
+ [Kk]*) __bshift=10 ;;
+ [Mm]*) __bshift=20 ;;
+ [Gg]*) __bshift=30 ;;
+ [Tt]*) __bshift=40 ;;
+ [Pp]*) __bshift=50 ;;
+ [Ee]*) __bshift=60 ;;
+ *)
+ # Unknown prefix
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" -1
+ else
+ echo -1
+ fi
+ return 2 # 2 = "An unrecognized prefix was given"
+ esac
+
+ # Determine if the wheels fall off
+ __maxinput=$(( 0x7fffffffffffffff >> $__bshift ))
+ if [ $__num -gt $__maxinput ]; then
+ # Input (before expanding) would exceed 64-bit signed int
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" -1
+ else
+ echo -1
+ fi
+ return 3 # 3 = "Result too large to calculate"
+ fi
+
+ # Shift the number out and produce it
+ __num=$(( $__num << $__bshift ))
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" $__num
+ else
+ echo $__num
+ fi
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." strings.subr
+
+fi # ! $_STRINGS_SUBR
diff --git a/usr.sbin/bsdconfig/share/struct.subr b/usr.sbin/bsdconfig/share/struct.subr
new file mode 100644
index 0000000..78c785f
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/struct.subr
@@ -0,0 +1,206 @@
+if [ ! "$_STRUCT_SUBR" ]; then _STRUCT_SUBR=1
+#
+# Copyright (c) 2012-2013 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+
+############################################################ FUNCTIONS
+
+# f_struct_define $type $member_name1 ...
+#
+# Define a new `structure' type $type made up of the properties $member_name1
+# $member_name2 and so-on. Properties are not typed and can hold any type of
+# data (including names of other structs).
+#
+# Before creating instances of a struct (using f_struct_new $type $name) you
+# should use this function to define $type.
+#
+# Both $type and member names should consist only of alpha-numeric letters or
+# the underscore.
+#
+f_struct_define()
+{
+ local type="$1"
+ [ "$type" ] || return $FAILURE
+ shift
+ setvar "_struct_typedef_$type" "$*"
+}
+
+# f_struct_new $type $name
+#
+# Create a new `structure' named $name of type $type. There are two ways to
+# access properties of a struct, but they are not equal (each method has its
+# own unique benefits, discussed below).
+#
+# The primary method of accessing (both setting and getting) properties of any
+# struct is through the f_struct() function below.
+#
+# The secondary method of accessing data is by using $name as a function.
+#
+# Both access methods are cross-platform compatible with any version of sh(1).
+# Below is an example of the primary access method:
+#
+# f_struct_new MY_STRUCT_TYPE my_struct
+# f_struct my_struct set abc 123
+# f_struct my_struct get abc # prints 123 to stdout
+# f_struct my_struct get abc abc # sets local variable $abc to 123
+#
+# Alternatively, the secondary access method (details below):
+#
+# f_struct_new MY_STRUCT_TYPE my_struct
+# my_struct set abc 123
+# my_struct get abc # prints 123 to stdout
+# my_struct get abc abc # sets local variable $abc to 123
+#
+# The secondary form should only be used if/when:
+# + You are certain that the structure already exists
+# + You want a syntax error if/when the struct does not exist
+#
+# The primary benefit to the secondary form is syntax cleanliness and read-
+# ability. If you are unsure if a given struct exists (which would cause a
+# syntax error when using this form), you can use the primary access method to
+# first test for the existence of the struct. For example:
+#
+# if f_struct my_struct; then
+# my_struct get abc # only executed if my_struct exists
+# fi
+#
+# For more information, see the f_struct() function.
+#
+f_struct_new()
+{
+ local type="$1" name="$2"
+ f_dprintf "f_struct_new: type=[%s] name=[%s]" "$type" "$name"
+ [ "$name" ] || return $FAILURE
+ setvar "_struct_type_$name" "$type" || return $FAILURE
+ # OK to use bare $name at this point
+ eval $name\(\){ f_struct $name \"\$@\"\; }
+}
+
+# f_struct $name
+# f_struct $name get $property [$var_to_set]
+# f_struct $name set $property $new_value
+# f_struct $name unset $property
+#
+# Access routine for getting, setting, unsetting, and testing properties of
+# `structures'.
+#
+# If only given $name, returns success if struct $name has been created (using
+# the f_struct_new() function above).
+#
+# For getting properties of a struct (versus setting) there are two methods of
+# access. If $var_to_set is missing or NULL, the value of the property 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). Returns success unless the property is unset.
+#
+# For setting properties of a struct, sets the value of $property to $new_value
+# and returns success.
+#
+# For unsetting, the underlying environment variable associated with the given
+# $property is unset.
+#
+f_struct()
+{
+ local __name="$1" __action="$2" __property="$3"
+ case $# in
+ 0) return $FAILURE ;;
+ 1) f_have "$__name" ;;
+ *) case "$__action" in
+ get) local __var_to_set="$4"
+ f_getvar "_struct_value_${__name}_$__property" "$__var_to_set"
+ ;;
+ set) local new_value="$4"
+ setvar "_struct_value_${__name}_$__property" "$new_value" ;;
+ unset) unset "_struct_value_${__name}_$__property" ;;
+ esac
+ esac
+ # Return the status of the last command above
+}
+
+# f_struct_free $name
+#
+# Unset the collection of environment variables and accessor-function
+# associated with struct $name.
+#
+f_struct_free()
+{
+ local name="$1" type member members
+ f_getvar "_struct_type_$name" type
+ f_dprintf "f_struct_free: name=[%s] type=[%s]" "$name" "$type"
+ [ "$name" ] || return $FAILURE
+ f_getvar "_struct_typedef_$type" members
+ for member in $members; do
+ f_struct "$name" unset $member
+ done
+ unset -f "$name"
+ unset "_struct_type_$name"
+}
+
+# f_struct_copy $from_name $to_name
+#
+# Copy the properties of one struct to another. If struct $to_name does not
+# exist, it is created. If struct $from_name does not exist, nothing is done
+# and struct $to_name remains unmodified.
+#
+# Returns success unless struct $to_name did not exist and f_struct_new() was
+# unable to create it.
+#
+f_struct_copy()
+{
+ local from_name="$1" to_name="$2" type
+ f_dprintf "f_struct_copy: from_name=[%s] to_name=[%s]" \
+ "$from_name" "$to_name"
+ f_getvar "_struct_type_$from_name" type
+ f_struct "$to_name" ||
+ f_struct_new "$type" "$to_name" || return $FAILURE
+ f_struct "$from_name" || return $SUCCESS
+ f_dprintf "f_struct_copy: copying properties from %s to %s" \
+ "$from_name" "$to_name"
+ local property properties from_value n=0 k=0
+ f_getvar "_struct_typedef_$type" properties
+ for property in $properties; do
+ k=$(( $k + 1 ))
+ if f_struct "$from_name" get $property from_value; then
+ f_struct "$to_name" set $property "$from_value"
+ n=$(( $n + 1 ))
+ else
+ f_struct "$to_name" unset $property
+ fi
+ done
+ f_dprintf "f_struct_copy: copied %u of %u properties from %s to %s" \
+ "$n" "$k" "$from_name" "$to_name"
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." struct.subr
+
+fi # ! $_STRUCT_SUBR
diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr
new file mode 100644
index 0000000..346bf10
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/sysrc.subr
@@ -0,0 +1,746 @@
+if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1
+#
+# Copyright (c) 2006-2015 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
+
+BSDCFG_LIBE="/usr/libexec/bsdconfig"
+if [ ! "$_SYSRC_JAILED" ]; then
+ f_dprintf "%s: loading includes..." sysrc.subr
+ f_include_lang $BSDCFG_LIBE/include/messages.subr
+fi
+
+############################################################ CONFIGURATION
+
+#
+# Standard pathnames (inherit values from shell if available)
+#
+: ${RC_DEFAULTS:="/etc/defaults/rc.conf"}
+
+############################################################ GLOBALS
+
+#
+# Global exit status variables
+#
+SUCCESS=0
+FAILURE=1
+
+#
+# Valid characters that can appear in an sh(1) variable name
+#
+# Please note that the character ranges A-Z and a-z should be avoided because
+# these can include accent characters (which are not valid in a variable name).
+# For example, A-Z matches any character that sorts after A but before Z,
+# including A and Z. Although ASCII order would make more sense, that is not
+# how it works.
+#
+VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
+
+############################################################ FUNCTIONS
+
+# f_clean_env [ --except $varname ... ]
+#
+# Unset all environment variables in the current scope. An optional list of
+# arguments can be passed, indicating which variables to avoid unsetting; the
+# `--except' is required to enable the exclusion-list as the remainder of
+# positional arguments.
+#
+# Be careful not to call this in a shell that you still expect to perform
+# $PATH expansion in, because this will blow $PATH away. This is best used
+# within a sub-shell block "(...)" or "$(...)" or "`...`".
+#
+f_clean_env()
+{
+ local var arg except=
+
+ #
+ # Should we process an exclusion-list?
+ #
+ if [ "$1" = "--except" ]; then
+ except=1
+ shift 1
+ fi
+
+ #
+ # Loop over a list of variable names from set(1) built-in.
+ #
+ for var in $( set | awk -F= \
+ '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \
+ | grep -v '^except$'
+ ); do
+ #
+ # In POSIX bourne-shell, attempting to unset(1) OPTIND results
+ # in "unset: Illegal number:" and causes abrupt termination.
+ #
+ [ "$var" = OPTIND ] && continue
+
+ #
+ # Process the exclusion-list?
+ #
+ if [ "$except" ]; then
+ for arg in "$@" ""; do
+ [ "$var" = "$arg" ] && break
+ done
+ [ "$arg" ] && continue
+ fi
+
+ unset "$var"
+ done
+}
+
+# f_sysrc_get $varname
+#
+# Get a system configuration setting from the collection of system-
+# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and
+# /etc/rc.conf.local)
+#
+# NOTE: Additional shell parameter-expansion formats are supported. For
+# example, passing an argument of "hostname%%.*" (properly quoted) will
+# return the hostname up to (but not including) the first `.' (see sh(1),
+# "Parameter Expansion" for more information on additional formats).
+#
+f_sysrc_get()
+{
+ # Sanity check
+ [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
+
+ # Taint-check variable name
+ case "$1" in
+ [0-9]*)
+ # Don't expand possible positional parameters
+ return $FAILURE ;;
+ *)
+ [ "$1" ] || return $FAILURE
+ esac
+
+ ( # Execute within sub-shell to protect parent environment
+
+ #
+ # Clear the environment of all variables, preventing the
+ # expansion of normals such as `PS1', `TERM', etc.
+ #
+ f_clean_env --except IFS RC_CONFS RC_DEFAULTS
+
+ . "$RC_DEFAULTS" > /dev/null 2>&1
+
+ unset RC_DEFAULTS
+ # no longer needed
+
+ #
+ # If the query is for `rc_conf_files' then store the value that
+ # we inherited from sourcing RC_DEFAULTS (above) so that we may
+ # conditionally restore this value after source_rc_confs in the
+ # event that RC_CONFS does not customize the value.
+ #
+ if [ "$1" = "rc_conf_files" ]; then
+ _rc_conf_files="$rc_conf_files"
+ fi
+
+ #
+ # If RC_CONFS is defined, set $rc_conf_files to an explicit
+ # value, modifying the default behavior of source_rc_confs().
+ #
+ if [ "${RC_CONFS+set}" ]; then
+ rc_conf_files="$RC_CONFS"
+ _rc_confs_set=1
+ fi
+
+ source_rc_confs > /dev/null 2>&1
+
+ #
+ # If the query was for `rc_conf_files' AND after calling
+ # source_rc_confs the value has not changed, then we should
+ # restore the value to the one inherited from RC_DEFAULTS
+ # before performing the final query (preventing us from
+ # returning what was set via RC_CONFS when the intent was
+ # instead to query the value from the file(s) specified).
+ #
+ if [ "$1" = "rc_conf_files" -a \
+ "$_rc_confs_set" -a \
+ "$rc_conf_files" = "$RC_CONFS" \
+ ]; then
+ rc_conf_files="$_rc_conf_files"
+ unset _rc_conf_files
+ unset _rc_confs_set
+ fi
+
+ unset RC_CONFS
+ # no longer needed
+
+ #
+ # This must be the last functional line for both the sub-shell
+ # and the function to preserve the return status from formats
+ # such as "${varname?}" and "${varname:?}" (see "Parameter
+ # Expansion" in sh(1) for more information).
+ #
+ eval echo '"${'"$1"'}"' 2> /dev/null
+ )
+}
+
+# f_sysrc_service_configs [-a|-p] $name [$var_to_set]
+#
+# Get a list of optional `rc.conf.d' entries sourced by system `rc.d' script
+# $name (see rc.subr(8) for additional information on `rc.conf.d'). If $name
+# exists in `/etc/rc.d' or $local_startup directories and is an rc(8) script
+# the result is a space separated list of `rc.conf.d' entries sourced by the
+# $name `rc.d' script. Otherwise, if $name exists as a binary `rc.d' script,
+# the result is ``/etc/rc.conf.d/$name /usr/local/etc/rc.conf.d/$name''. The
+# result is NULL if $name does not exist.
+#
+# If $var_to_set is missing or NULL, output is to standard out. Returns success
+# if $name was found, failure otherwise.
+#
+# If `-a' flag is given and $var_to_set is non-NULL, append result to value of
+# $var_to_set rather than overwriting current contents.
+#
+# If `-p' flag is given and $var_to_set is non-NULL, prepend result to value of
+# $var_to_set rather than overwriting current contents.
+#
+# NB: The `-a' and `-p' option flags are mutually exclusive.
+#
+f_sysrc_service_configs()
+{
+ local OPTIND=1 OPTARG __flag __append= __prepend=
+ local __local_startup __dir __spath __stype __names=
+
+ while getopts ap __flag; do
+ case "$__flag" in
+ a) __append=1 __prepend= ;;
+ p) __prepend=1 __append= ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ [ $# -gt 0 ] || return $FAILURE
+ local __sname="$1" __var_to_set="$2"
+
+ __local_startup=$( f_sysrc_get local_startup )
+ for __dir in /etc/rc.d $__local_startup; do
+ __spath="$__dir/$__sname"
+ [ -f "$__spath" -a -x "$__spath" ] || __spath= continue
+ break
+ done
+ [ "$__spath" ] || return $FAILURE
+
+ __stype=$( file -b "$__spath" 2> /dev/null )
+ case "$__stype" in
+ *"shell script"*)
+ __names=$( exec 9<&1 1>&- 2>&-
+ last_name=
+ print_name() {
+ local name="$1"
+ [ "$name" = "$last_name" ] && return
+ echo "$name" >&9
+ last_name="$name"
+ }
+ eval "$( awk '{
+ gsub(/load_rc_config /, "print_name ")
+ gsub(/run_rc_command /, ": ")
+ print
+ }' "$__spath" )"
+ ) ;;
+ *)
+ __names="$__sname"
+ esac
+
+ local __name __test_path __configs=
+ for __name in $__names; do
+ for __dir in /etc/rc.d $__local_startup; do
+ __test_path="${__dir%/rc.d}/rc.conf.d/$__name"
+ [ -d "$__test_path" ] ||
+ __configs="$__configs $__test_path" continue
+ for __test_path in "$__test_path"/*; do
+ [ -f "$__test_path" ] || continue
+ __configs="$__configs $__test_path"
+ done
+ done
+ done
+ __configs="${__configs# }"
+
+ if [ "$__var_to_set" ]; then
+ local __cur=
+ [ "$__append" -o "$__prepend" ] &&
+ f_getvar "$__var_to_set" __cur
+ [ "$__append" ] && __configs="$__cur{$__cur:+ }$__configs"
+ [ "$__prepend" ] && __configs="$__configs${__cur:+ }$__cur"
+ setvar "$__var_to_set" "$__configs"
+ else
+ echo "$__configs"
+ fi
+
+ return $SUCCESS
+}
+
+# f_sysrc_get_default $varname
+#
+# Get a system configuration default setting from the default rc.conf(5) file
+# (or whatever RC_DEFAULTS points at).
+#
+f_sysrc_get_default()
+{
+ # Sanity check
+ [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE
+
+ # Taint-check variable name
+ case "$1" in
+ [0-9]*)
+ # Don't expand possible positional parameters
+ return $FAILURE ;;
+ *)
+ [ "$1" ] || return $FAILURE
+ esac
+
+ ( # Execute within sub-shell to protect parent environment
+
+ #
+ # Clear the environment of all variables, preventing the
+ # expansion of normals such as `PS1', `TERM', etc.
+ #
+ f_clean_env --except RC_DEFAULTS
+
+ . "$RC_DEFAULTS" > /dev/null 2>&1
+
+ unset RC_DEFAULTS
+ # no longer needed
+
+ #
+ # This must be the last functional line for both the sub-shell
+ # and the function to preserve the return status from formats
+ # such as "${varname?}" and "${varname:?}" (see "Parameter
+ # Expansion" in sh(1) for more information).
+ #
+ eval 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%%[!$VALID_VARNAME_CHARS]*}"
+ local regex="^[[:space:]]*$varname="
+ local rc_conf_files="$( f_sysrc_get rc_conf_files )"
+ local conf_files=
+ local file
+
+ # Check parameters
+ case "$varname" in
+ ""|[0-9]*) return $FAILURE
+ esac
+
+ #
+ # If RC_CONFS is defined, set $rc_conf_files to an explicit
+ # value, modifying the default behavior of source_rc_confs().
+ #
+ [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS"
+
+ #
+ # Reverse the order of files in rc_conf_files (the boot process sources
+ # these in order, so we will search them in reverse-order to find the
+ # last-assignment -- the one that ultimately effects the environment).
+ #
+ for file in $rc_conf_files; do
+ conf_files="$file${conf_files:+ }$conf_files"
+ done
+
+ #
+ # Append the defaults file (since directives in the defaults file
+ # indeed affect the boot process, we'll want to know when a directive
+ # is found there).
+ #
+ conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS"
+
+ #
+ # Find which file matches assignment to the given variable name.
+ #
+ for file in $conf_files; do
+ [ -f "$file" -a -r "$file" ] || continue
+ if grep -Eq "$regex" $file; then
+ echo $file
+ return $SUCCESS
+ fi
+ done
+
+ return $FAILURE # Not found
+}
+
+# f_sysrc_desc $varname
+#
+# Attempts to return the comments associated with varname from the rc.conf(5)
+# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to).
+#
+# Multi-line comments are joined together. Results are NULL if no description
+# could be found.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_desc_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = 0
+ buffer = ""
+}
+{
+ if ( ! found )
+ {
+ if ( ! match($0, regex) ) next
+
+ found = 1
+ sub(/^[^#]*(#[[:space:]]*)?/, "")
+ buffer = $0
+ next
+ }
+
+ if ( !/^[[:space:]]*#/ ||
+ /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ ||
+ /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ ||
+ /^[[:space:]]*$/ ) exit
+
+ sub(/(.*#)*[[:space:]]*/, "")
+ buffer = buffer" "$0
+}
+END {
+ # Clean up the buffer
+ sub(/^[[:space:]]*/, "", buffer)
+ sub(/[[:space:]]*$/, "", buffer)
+
+ print buffer
+ exit ! found
+}
+'
+f_sysrc_desc()
+{
+ awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS"
+}
+
+# f_sysrc_set $varname $new_value
+#
+# Change a setting in the system configuration files (edits the files in-place
+# to change the value in the last assignment to the variable). If the variable
+# does not appear in the source file, it is appended to the end of the primary
+# system configuration file `/etc/rc.conf'.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_set_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+# -v new_value="new_value"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = retval = 0
+}
+{
+ # If already found... just spew
+ if ( found ) { print; next }
+
+ # Does this line match an assignment to our variable?
+ if ( ! match($0, regex) ) { print; next }
+
+ # Save important match information
+ found = 1
+ matchlen = RSTART + RLENGTH - 1
+
+ # Store the value text for later munging
+ value = substr($0, matchlen + 1, length($0) - matchlen)
+
+ # Store the first character of the value
+ t1 = t2 = substr(value, 0, 1)
+
+ # Assignment w/ back-ticks, expression, or misc.
+ # We ignore these since we did not generate them
+ #
+ if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next }
+
+ # Assignment w/ single-quoted value
+ else if ( t1 == "'\''" ) {
+ sub(/^'\''[^'\'']*/, "", value)
+ if ( length(value) == 0 ) t2 = ""
+ sub(/^'\''/, "", value)
+ }
+
+ # Assignment w/ double-quoted value
+ else if ( t1 == "\"" ) {
+ sub(/^"(.*\\\\+")*[^"]*/, "", value)
+ if ( length(value) == 0 ) t2 = ""
+ sub(/^"/, "", value)
+ }
+
+ # Assignment w/ non-quoted value
+ else if ( t1 ~ /[^[:space:];]/ ) {
+ t1 = t2 = "\""
+ sub(/^[^[:space:]]*/, "", value)
+ }
+
+ # Null-assignment
+ else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" }
+
+ printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \
+ t1, new_value, t2, value
+}
+END { exit retval }
+'
+f_sysrc_set()
+{
+ local funcname=f_sysrc_set
+ local varname="$1" new_value="$2"
+
+ # Check arguments
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # Find which rc.conf(5) file contains the last-assignment
+ #
+ local not_found=
+ local file="$( f_sysrc_find "$varname" )"
+ if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then
+ #
+ # We either got a null response (not found) or the variable
+ # was only found in the rc.conf(5) defaults. In either case,
+ # let's instead modify the first file from $rc_conf_files.
+ #
+
+ not_found=1
+
+ #
+ # If RC_CONFS is defined, use $RC_CONFS
+ # rather than $rc_conf_files.
+ #
+ if [ "${RC_CONFS+set}" ]; then
+ file="${RC_CONFS%%[$IFS]*}"
+ else
+ file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
+ fi
+ fi
+
+ #
+ # If not found, append new value to 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
+ if ! f_eval_catch -dk tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm"
+ then
+ echo "$tmpfile" >&2
+ return $FAILURE
+ fi
+
+ #
+ # Fixup permissions (else we're in for a surprise, as mktemp(1) creates
+ # the temporary file with 0600 permissions, and if we simply mv(1) the
+ # temporary file over the destination, the destination will inherit the
+ # permissions from the temporary file).
+ #
+ local mode
+ f_eval_catch -dk mode $funcname stat 'stat -f "%%#Lp" "%s"' "$file" ||
+ mode=0644
+ f_eval_catch -d $funcname chmod 'chmod "%s" "%s"' "$mode" "$tmpfile"
+
+ #
+ # Fixup ownership. The destination file _is_ writable (we tested
+ # earlier above). However, this will fail if we don't have sufficient
+ # permissions (so we throw stderr into the bit-bucket).
+ #
+ local owner
+ f_eval_catch -dk owner $funcname stat \
+ 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
+ f_eval_catch -d $funcname chown 'chown "%s" "%s"' "$owner" "$tmpfile"
+
+ #
+ # Operate on the matching file, replacing only the last occurrence.
+ #
+ 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 ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"; then
+ f_err "$msg_previous_syntax_errors\n" "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Finally, move the temporary file into place.
+ #
+ f_eval_catch -de $funcname mv 'mv "%s" "%s"' "$tmpfile" "$file"
+}
+
+# f_sysrc_delete $varname
+#
+# Remove a setting from the system configuration files (edits files in-place).
+# Deletes all assignments to the given variable in all config files. If the
+# `-f file' option is passed, the removal is restricted to only those files
+# specified, otherwise the system collection of rc_conf_files is used.
+#
+# This function is a two-parter. Below is the awk(1) portion of the function,
+# afterward is the sh(1) function which utilizes the below awk script.
+#
+f_sysrc_delete_awk='
+# Variables that should be defined on the invocation line:
+# -v varname="varname"
+#
+BEGIN {
+ regex = "^[[:space:]]*"varname"="
+ found = 0
+}
+{
+ if ( $0 ~ regex )
+ found = 1
+ else
+ print
+}
+END { exit ! found }
+'
+f_sysrc_delete()
+{
+ local funcname=f_sysrc_delete
+ local varname="$1"
+ local file
+
+ # Check arguments
+ [ "$varname" ] || return $FAILURE
+
+ #
+ # Operate on each of the specified files
+ #
+ local tmpfile
+ for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do
+ [ -e "$file" ] || continue
+
+ #
+ # Create a new temporary file to write to.
+ #
+ if ! f_eval_catch -dk tmpfile $funcname mktemp \
+ 'mktemp -t "%s"' "$pgm"
+ then
+ echo "$tmpfile" >&2
+ return $FAILURE
+ fi
+
+ #
+ # Fixup permissions and ownership (mktemp(1) defaults to 0600
+ # permissions) to instead match the destination file.
+ #
+ local mode owner
+ f_eval_catch -dk mode $funcname stat \
+ 'stat -f "%%#Lp" "%s"' "$file" || mode=0644
+ f_eval_catch -dk owner $funcname stat \
+ 'stat -f "%%u:%%g" "%s"' "$file" || owner="root:wheel"
+ f_eval_catch -d $funcname chmod \
+ 'chmod "%s" "%s"' "$mode" "$tmpfile"
+ f_eval_catch -d $funcname chown \
+ 'chown "%s" "%s"' "$owner" "$tmpfile"
+
+ #
+ # Operate on the file, removing all occurrences, saving the
+ # output in our temporary file.
+ #
+ awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \
+ > "$tmpfile"
+ if [ $? -ne $SUCCESS ]; then
+ # The file didn't contain any assignments
+ rm -f "$tmpfile"
+ continue
+ fi
+
+ #
+ # Taint-check our results.
+ #
+ if ! f_eval_catch -d $funcname sh '/bin/sh -n "%s"' "$tmpfile"
+ then
+ f_err "$msg_previous_syntax_errors\n" \
+ "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Perform sanity checks
+ #
+ if [ ! -w "$file" ]; then
+ f_err "$msg_permission_denied\n" "$pgm" "$file"
+ rm -f "$tmpfile"
+ return $FAILURE
+ fi
+
+ #
+ # Finally, move the temporary file into place.
+ #
+ f_eval_catch -de $funcname mv \
+ 'mv "%s" "%s"' "$tmpfile" "$file" || return $FAILURE
+ done
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." sysrc.subr
+
+fi # ! $_SYSRC_SUBR
diff --git a/usr.sbin/bsdconfig/share/variable.subr b/usr.sbin/bsdconfig/share/variable.subr
new file mode 100644
index 0000000..c453f67
--- /dev/null
+++ b/usr.sbin/bsdconfig/share/variable.subr
@@ -0,0 +1,315 @@
+if [ ! "$_VARIABLE_SUBR" ]; then _VARIABLE_SUBR=1
+#
+# Copyright (c) 2012-2014 Devin Teske
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." variable.subr
+f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/strings.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
+ f_dialog_input cp "$( printf "$@" )" "$cp" && setvar $var "$cp"
+ fi
+
+ return $SUCCESS
+}
+
+# f_variable_set_defaults
+#
+# Installs sensible defaults for registered/advertised variables.
+#
+f_variable_set_defaults()
+{
+ f_dprintf "f_variable_set_defaults: Initializing defaults..."
+
+ #
+ # Initialize various user-edittable values to their defaults
+ #
+ setvar $VAR_EDITOR "${EDITOR:-/usr/bin/ee}"
+ setvar $VAR_FTP_STATE "auto"
+ setvar $VAR_FTP_USER "ftp"
+ setvar $VAR_HOSTNAME "$( hostname )"
+ setvar $VAR_MEDIA_TIMEOUT "300"
+ setvar $VAR_NFS_SECURE "NO"
+ setvar $VAR_NFS_TCP "NO"
+ setvar $VAR_NFS_V3 "YES"
+ setvar $VAR_PKG_TMPDIR "/var/tmp"
+ setvar $VAR_RELNAME "$UNAME_R"
+
+ #
+ # Debugging
+ #
+ if f_debugging; then
+ local var
+ for var in \
+ $VAR_EDITOR \
+ $VAR_FTP_STATE \
+ $VAR_FTP_USER \
+ $VAR_HOSTNAME \
+ $VAR_MEDIA_TIMEOUT \
+ $VAR_NFS_SECURE \
+ $VAR_NFS_TCP \
+ $VAR_NFS_V3 \
+ $VAR_PKG_TMPDIR \
+ $VAR_RELNAME \
+ ; do
+ f_quietly f_getvar $var
+ done
+ fi
+
+ 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_dialog_msgbox() (from dialog.subr).
+#
+f_dump_variables()
+{
+ local err
+ if ! err=$(
+ ( for handle in $VARIABLES; do
+ f_getvar $handle var || continue
+ f_getvar $var value || continue
+ f_shell_escape "$value" value
+ printf "%s='%s'\n" "$var" "$value"
+ done > "$VARIABLE_DUMPFILE" ) 2>&1
+ ); then
+ f_dialog_msgbox "$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" ]
+}
+
+# f_netinteractive
+#
+# Has the user specifically requested the network-portion of configuration and
+# setup to be performed interactively? Returns success if the user has asked
+# for the network configuration to be done interactively even if perhaps over-
+# all non-interactive mode has been requested (by setting nonInteractive).
+#
+# Returns success if $netInteractive is set and non-NULL.
+#
+f_netinteractive()
+{
+ local value
+ f_getvar $VAR_NETINTERACTIVE value && [ "$value" ]
+}
+
+# f_zfsinteractive
+#
+# Has the user specifically requested the ZFS-portion of configuration and
+# setup to be performed interactively? Returns success if the user has asked
+# for the ZFS configuration to be done interactively even if perhaps overall
+# non-interactive mode has been requested (by setting nonInteractive).
+#
+# Returns success if $zfsInteractive is set and non-NULL.
+#
+f_zfsinteractive()
+{
+ local value
+ f_getvar $VAR_ZFSINTERACTIVE value && [ "$value" ]
+}
+
+############################################################ MAIN
+
+#
+# Variables that can be tweaked from config files
+#
+# Handle Variable Name
+f_variable_new VAR_CONFIG_FILE configFile
+f_variable_new VAR_DEBUG debug
+f_variable_new VAR_DEBUG_FILE debugFile
+f_variable_new VAR_DIRECTORY_PATH _directoryPath
+f_variable_new VAR_DOMAINNAME domainname
+f_variable_new VAR_EDITOR editor
+f_variable_new VAR_EXTRAS ifconfig_
+f_variable_new VAR_FTP_DIR ftpDirectory
+f_variable_new VAR_FTP_HOST ftpHost
+f_variable_new VAR_FTP_PASS ftpPass
+f_variable_new VAR_FTP_PATH _ftpPath
+f_variable_new VAR_FTP_PORT ftpPort
+f_variable_new VAR_FTP_STATE ftpState
+f_variable_new VAR_FTP_USER ftpUser
+f_variable_new VAR_GATEWAY defaultrouter
+f_variable_new VAR_GROUP group
+f_variable_new VAR_GROUP_GID groupGid
+f_variable_new VAR_GROUP_MEMBERS groupMembers
+f_variable_new VAR_GROUP_PASSWORD groupPassword
+f_variable_new VAR_HOSTNAME hostname
+f_variable_new VAR_HTTP_DIR httpDirectory
+f_variable_new VAR_HTTP_FTP_MODE httpFtpMode
+f_variable_new VAR_HTTP_HOST httpHost
+f_variable_new VAR_HTTP_PATH _httpPath
+f_variable_new VAR_HTTP_PORT httpPort
+f_variable_new VAR_HTTP_PROXY httpProxy
+f_variable_new VAR_HTTP_PROXY_HOST httpProxyHost
+f_variable_new VAR_HTTP_PROXY_PATH _httpProxyPath
+f_variable_new VAR_HTTP_PROXY_PORT httpProxyPort
+f_variable_new VAR_IFCONFIG ifconfig_
+f_variable_new VAR_IPADDR ipaddr
+f_variable_new VAR_IPV6ADDR ipv6addr
+f_variable_new VAR_IPV6_ENABLE ipv6_activate_all_interfaces
+f_variable_new VAR_KEYMAP keymap
+f_variable_new VAR_MEDIA_TIMEOUT MEDIA_TIMEOUT
+f_variable_new VAR_MEDIA_TYPE mediaType
+f_variable_new VAR_NAMESERVER nameserver
+f_variable_new VAR_NETINTERACTIVE netInteractive
+f_variable_new VAR_NETMASK netmask
+f_variable_new VAR_NETWORK_DEVICE netDev
+f_variable_new VAR_NFS_HOST nfsHost
+f_variable_new VAR_NFS_PATH nfsPath
+f_variable_new VAR_NFS_SECURE nfs_reserved_port_only
+f_variable_new VAR_NFS_TCP nfs_use_tcp
+f_variable_new VAR_NFS_V3 nfs_use_v3
+f_variable_new VAR_NONINTERACTIVE nonInteractive
+f_variable_new VAR_NO_CONFIRM noConfirm
+f_variable_new VAR_NO_ERROR noError
+f_variable_new VAR_NO_INET6 noInet6
+f_variable_new VAR_PACKAGE package
+f_variable_new VAR_PKG_TMPDIR PKG_TMPDIR
+f_variable_new VAR_PORTS_PATH ports
+f_variable_new VAR_RELNAME releaseName
+f_variable_new VAR_SLOW_ETHER slowEthernetCard
+f_variable_new VAR_TRY_DHCP tryDHCP
+f_variable_new VAR_TRY_RTSOL tryRTSOL
+f_variable_new VAR_UFS_PATH ufs
+f_variable_new VAR_USER user
+f_variable_new VAR_USER_ACCOUNT_EXPIRE userAccountExpire
+f_variable_new VAR_USER_DOTFILES_CREATE userDotfilesCreate
+f_variable_new VAR_USER_GECOS userGecos
+f_variable_new VAR_USER_GID userGid
+f_variable_new VAR_USER_GROUPS userGroups
+f_variable_new VAR_USER_GROUP_DELETE userGroupDelete
+f_variable_new VAR_USER_HOME userHome
+f_variable_new VAR_USER_HOME_CREATE userHomeCreate
+f_variable_new VAR_USER_HOME_DELETE userHomeDelete
+f_variable_new VAR_USER_LOGIN_CLASS userLoginClass
+f_variable_new VAR_USER_PASSWORD userPassword
+f_variable_new VAR_USER_PASSWORD_EXPIRE userPasswordExpire
+f_variable_new VAR_USER_SHELL userShell
+f_variable_new VAR_USER_UID userUid
+f_variable_new VAR_ZFSINTERACTIVE zfsInteractive
+
+#
+# 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
OpenPOWER on IntegriCloud