1if [ ! "$_STARTUP_RCCONF_SUBR" ]; then _STARTUP_RCCONF_SUBR=1 2# 3# Copyright (c) 2006-2013 Devin Teske 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 28# 29############################################################ INCLUDES 30 31BSDCFG_SHARE="/usr/share/bsdconfig" 32. $BSDCFG_SHARE/common.subr || exit 1 33f_dprintf "%s: loading includes..." startup/rcconf.subr 34f_include $BSDCFG_SHARE/sysrc.subr 35 36BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" 37f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr 38 39############################################################ GLOBALS 40 41# 42# Initialize in-memory cache variables 43# 44STARTUP_RCCONF_MAP= 45_STARTUP_RCCONF_MAP= 46 47# 48# Define what a variable looks like 49# 50STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" 51 52# 53# Default path to on-disk cache file(s) 54# 55STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" 56 57############################################################ FUNCTIONS 58 59# f_startup_rcconf_list 60# 61# Produce a list of non-default configuration variables configured in the 62# rc.conf(5) collection of files. 63# 64f_startup_rcconf_list() 65{ 66 ( # Operate within a sub-shell to protect the parent environment 67 . "$RC_DEFAULTS" > /dev/null 68 f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files 69 source_rc_confs > /dev/null 70 export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" 71 export RC_DEFAULTS 72 set | awk -F= " 73 function test_print(var) 74 { 75 if ( var == \"OPTIND\" ) return 76 if ( var == \"PATH\" ) return 77 if ( var == \"RC_DEFAULTS\" ) return 78 if ( var == \"STARTUP_RCCONF_REGEX\" ) return 79 if ( var == \"_rc_conf_files_file\" ) return 80 if ( var == \"rc_conf_files\" ) 81 { 82 if ( ENVIRON[\"_rc_conf_files_file\"] == \ 83 ENVIRON[\"RC_DEFAULTS\"] ) return 84 } 85 print var 86 } 87 /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" 88 ) 89} 90 91# f_startup_rcconf_map [$var_to_set] 92# 93# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) 94# variables and their descriptions. The map returned has the following format: 95# 96# var description 97# 98# With each as follows: 99# 100# var the rc.conf(5) variable 101# description description of the variable 102# 103# If $var_to_set is missing or NULL, the map is printed to standard output for 104# capturing in a sub-shell (which is less-recommended because of performance 105# degredation; for example, when called in a loop). 106# 107f_startup_rcconf_map() 108{ 109 local __funcname=f_startup_rcconf_map 110 local __var_to_set="$1" 111 112 # If the in-memory cached value is available, return it immediately 113 if [ "$_STARTUP_RCCONF_MAP" ]; then 114 if [ "$__var_to_set" ]; then 115 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 116 else 117 echo "$STARTUP_RCCONF_MAP" 118 fi 119 return $SUCCESS 120 fi 121 122 # 123 # Create the in-memory cache (potentially from validated on-disk cache) 124 # 125 126 # 127 # Calculate digest used to determine if the on-disk global persistent 128 # cache file (containing this digest on the first line) is valid and 129 # can be used to quickly populate the cache value for immediate return. 130 # 131 local __rc_defaults_digest 132 __rc_defaults_digest=$( exec 2> /dev/null; md5 < "$RC_DEFAULTS" ) 133 134 # 135 # Check to see if the global persistent cache file exists 136 # 137 if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then 138 # 139 # Attempt to populate the in-memory cache with the (soon to be) 140 # validated on-disk cache. If validation fails, fall-back to 141 # the current value and provide error exit status. 142 # 143 STARTUP_RCCONF_MAP=$( 144 ( # Get digest as the first word on first line 145 read digest rest_ignored 146 147 # 148 # If the stored digest matches the calculated- 149 # one populate the in-memory cache from the on- 150 # disk cache and provide success exit status. 151 # 152 if [ "$digest" = "$__rc_defaults_digest" ] 153 then 154 cat 155 exit $SUCCESS 156 else 157 # Otherwise, return the current value 158 echo "$STARTUP_RCCONF_MAP" 159 exit $FAILURE 160 fi 161 ) < "$STARTUP_RCCONF_MAP_CACHEFILE" 162 ) 163 local __retval=$? 164 export STARTUP_RCCONF_MAP # Make children faster (export cache) 165 if [ $__retval -eq $SUCCESS ]; then 166 export _STARTUP_RCCONF_MAP=1 167 if [ "$__var_to_set" ]; then 168 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 169 else 170 echo "$STARTUP_RCCONF_MAP" 171 fi 172 return $SUCCESS 173 fi 174 # Otherwise, fall-thru to create in-memory cache from scratch 175 fi 176 177 # 178 # If we reach this point, we need to generate the data from scratch 179 # (and after we do, we'll attempt to create the global persistent 180 # cache file to speed up future executions). 181 # 182 183 STARTUP_RCCONF_MAP=$( 184 f_clean_env --except \ 185 PATH \ 186 RC_DEFAULTS \ 187 STARTUP_RCCONF_REGEX \ 188 f_sysrc_desc_awk 189 . "$RC_DEFAULTS" 190 191 # Unset variables we don't want reported 192 unset source_rc_confs_defined 193 194 for var in $( set | awk -F= " 195 function test_print(var) 196 { 197 if ( var == \"OPTIND\" ) return 198 if ( var == \"PATH\" ) return 199 if ( var == \"RC_DEFAULTS\" ) return 200 if ( var == \"STARTUP_RCCONF_REGEX\" ) return 201 if ( var == \"f_sysrc_desc_awk\" ) return 202 print var 203 } 204 /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } 205 " ); do 206 echo $var "$( f_sysrc_desc $var )" 207 done 208 ) 209 export STARTUP_RCCONF_MAP 210 export _STARTUP_RCCONF_MAP=1 211 if [ "$__var_to_set" ]; then 212 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 213 else 214 echo "$STARTUP_RCCONF_MAP" 215 fi 216 217 # 218 # Attempt to create the persistent global cache 219 # 220 221 # Create a new temporary file to write to 222 local __tmpfile 223 f_eval_catch -dk __tmpfile $__funcname mktemp \ 224 'mktemp -t "%s"' "$pgm" || return $FAILURE 225 226 # Write the temporary file contents 227 echo "$__rc_defaults_digest" > "$__tmpfile" 228 echo "$STARTUP_RCCONF_MAP" >> "$__tmpfile" 229 230 # Finally, move the temporary file into place 231 case "$STARTUP_RCCONF_MAP_CACHEFILE" in 232 */*) f_eval_catch -d $__funcname mkdir \ 233 'mkdir -p "%s"' "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" 234 esac 235 f_eval_catch -d $__funcname mv \ 236 'mv "%s" "%s"' "$__tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" 237} 238 239# f_startup_rcconf_map_expand $var_to_get 240# 241# Expands the map ($var_to_get) into the shell environment namespace by 242# creating _${var}_desc variables containing the description of each variable 243# encountered. 244# 245# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. 246# 247f_startup_rcconf_map_expand() 248{ 249 local var_to_get="$1" 250 eval "$( debug= f_getvar "$var_to_get" | awk ' 251 BEGIN { 252 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 253 } 254 { 255 var = $1 256 desc = $0 257 sub(rword, "", desc) 258 gsub(/'\''/, "'\''\\'\'\''", desc) 259 printf "_%s_desc='\''%s'\''\n", var, desc 260 printf "export _%s_desc\n", var 261 }' )" 262} 263 264# f_dialog_input_view_details 265# 266# Display a menu for selecting which details are to be displayed. The following 267# variables are tracked/modified by the menu/user's selection: 268# 269# SHOW_DESC Show or hide descriptions 270# 271# Mutually exclusive options: 272# 273# SHOW_VALUE Show the value (default; override only) 274# SHOW_DEFAULT_VALUE Show both value and default 275# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in 276# 277# Each variable is treated as a boolean (NULL for false, non-NULL for true). 278# 279# Variables are exported for later-required awk(1) ENVIRON visibility. Returns 280# success unless the user chose `Cancel' or pressed Escape. 281# 282f_dialog_input_view_details() 283{ 284 local prompt= 285 local menu_list # calculated below 286 local defaultitem= # calculated below 287 local hline="$hline_arrows_tab_enter" 288 289 # Calculate marks for checkboxes and radio buttons 290 local md=" " 291 if [ "$SHOW_DESC" ]; then 292 md="X" 293 fi 294 local m1=" " m2=" " m3=" " 295 if [ "$SHOW_VALUE" ]; then 296 m1="*" 297 defaultitem="1 ($m1) $msg_show_value" 298 elif [ "$SHOW_DEFAULT_VALUE" ]; then 299 m2="*" 300 defaultitem="2 ($m2) $msg_show_default_value" 301 elif [ "$SHOW_CONFIGURED" ]; then 302 m3="*" 303 defaultitem="3 ($m3) $msg_show_configured" 304 fi 305 306 # Create the menu list with the above-calculated marks 307 menu_list=" 308 'R $msg_reset' '$msg_reset_desc' 309 'D [$md] $msg_desc' '$msg_desc_desc' 310 '1 ($m1) $msg_show_value' '$msg_show_value_desc' 311 '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' 312 '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' 313 " # END-QUOTE 314 315 local height width rows 316 eval f_dialog_menu_size height width rows \ 317 \"\$DIALOG_TITLE\" \ 318 \"\$DIALOG_BACKTITLE\" \ 319 \"\$prompt\" \ 320 \"\$hline\" \ 321 $menu_list 322 323 f_dialog_title "$msg_choose_view_details" 324 325 local mtag 326 mtag=$( eval $DIALOG \ 327 --title \"\$DIALOG_TITLE\" \ 328 --backtitle \"\$DIALOG_BACKTITLE\" \ 329 --hline \"\$hline\" \ 330 --ok-label \"\$msg_ok\" \ 331 --cancel-label \"\$msg_cancel\" \ 332 --default-item \"\$defaultitem\" \ 333 --menu \"\$prompt\" \ 334 $height $width $rows \ 335 $menu_list \ 336 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 337 ) 338 local retval=$? 339 f_dialog_data_sanitize mtag 340 341 f_dialog_title_restore 342 343 [ $retval -eq $DIALOG_OK ] || return $DIALOG_CANCEL 344 345 case "$mtag" in 346 "R $msg_reset") 347 SHOW_VALUE=1 348 SHOW_DESC=1 349 SHOW_DEFAULT_VALUE= 350 SHOW_CONFIGURED= 351 ;; 352 "D [X] $msg_desc") SHOW_DESC= ;; 353 "D [ ] $msg_desc") SHOW_DESC=1 ;; 354 "1 ("?") $msg_show_value") 355 SHOW_VALUE=1 356 SHOW_DEFAULT_VALUE= 357 SHOW_CONFIGURED= 358 ;; 359 "2 ("?") $msg_show_default_value") 360 SHOW_VALUE= 361 SHOW_DEFAULT_VALUE=1 362 SHOW_CONFIGURED= 363 ;; 364 "3 ("?") $msg_show_configured") 365 SHOW_VALUE= 366 SHOW_DEFAULT_VALUE= 367 SHOW_CONFIGURED=1 368 ;; 369 esac 370} 371 372# f_dialog_input_rclist [$default] 373# 374# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This 375# function should be treated like a call to dialog(1) (the exit status should 376# be captured and f_dialog_menutag_fetch() should be used to get the user's 377# response). Optionally if present and non-null, highlight $default rcvar. 378# 379f_dialog_input_rclist() 380{ 381 local prompt="$msg_please_select_an_rcconf_directive" 382 local menu_list=" 383 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_this_menu'} 384 " # END-QUOTE 385 local defaultitem="$1" 386 local hline="$hline_arrows_tab_enter" 387 388 if [ ! "$_RCCONF_MAP" ]; then 389 # Generate RCCONF_MAP of `var desc ...' per-line 390 f_dialog_info "$msg_creating_rcconf_map" 391 RCCONF_MAP=$( f_startup_rcconf_map ) 392 export RCCONF_MAP 393 # Generate _${var}_desc variables from $RCCONF_MAP 394 f_startup_rcconf_map_expand 395 export _RCCONF_MAP=1 396 fi 397 398 menu_list="$menu_list $( 399 export SHOW_DESC 400 echo "$RCCONF_MAP" | awk ' 401 BEGIN { 402 prefix = "" 403 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 404 } 405 { 406 cur_prefix = tolower(substr($1, 1, 1)) 407 printf "'\''" 408 if ( prefix != cur_prefix ) 409 prefix = cur_prefix 410 else 411 printf " " 412 rcvar = $1 413 printf "%s'\'' '\'\''", rcvar 414 if ( ENVIRON["SHOW_DESC"] ) { 415 desc = $0 416 sub(rword, "", desc) 417 gsub(/'\''/, "'\''\\'\'\''", desc) 418 printf " '\''%s'\''", desc 419 } 420 printf "\n" 421 }' 422 )" 423 424 set -f # set noglob because descriptions in the $menu_list may contain 425 # `*' and get expanded by dialog(1) (doesn't affect Xdialog(1)). 426 # This prevents dialog(1) from expanding wildcards in help line. 427 428 local height width rows 429 eval f_dialog_menu${SHOW_DESC:+_with_help}_size \ 430 height width rows \ 431 \"\$DIALOG_TITLE\" \ 432 \"\$DIALOG_BACKTITLE\" \ 433 \"\$prompt\" \ 434 \"\$hline\" \ 435 $menu_list 436 437 local menu_choice 438 menu_choice=$( eval $DIALOG \ 439 --title \"\$DIALOG_TITLE\" \ 440 --backtitle \"\$DIALOG_BACKTITLE\" \ 441 --hline \"\$hline\" \ 442 --default-item \"\$defaultitem\" \ 443 --ok-label \"\$msg_ok\" \ 444 --cancel-label \"\$msg_cancel\" \ 445 ${SHOW_DESC:+--item-help} \ 446 --menu \"\$prompt\" \ 447 $height $width $rows \ 448 $menu_list \ 449 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 450 ) 451 local retval=$? 452 f_dialog_menutag_store -s "$menu_choice" 453 return $retval 454} 455 456# f_dialog_input_rcvar [$init] 457# 458# Allows the user to enter the name for a new rc.conf(5) variable. If the user 459# does not cancel or press ESC, the $rcvar variable will hold the newly- 460# configured value upon return. 461# 462f_dialog_input_rcvar() 463{ 464 # 465 # Loop until the user provides taint-free/valid input 466 # 467 local _input="$1" 468 while :; do 469 470 # Return if user either pressed ESC or chosen Cancel/No 471 f_dialog_input _input "$msg_please_enter_rcvar_name" \ 472 "$_input" "$hline_alnum_tab_enter" || return $? 473 474 # Check for invalid entry (1of2) 475 if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then 476 f_show_msg "$msg_rcvar_must_start_with" 477 continue 478 fi 479 480 # Check for invalid entry (2of2) 481 if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" 482 then 483 f_show_msg "$msg_rcvar_contains_invalid_chars" 484 continue 485 fi 486 487 rcvar="$_input" 488 break 489 done 490 491 f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" 492 493 return $DIALOG_OK 494} 495 496############################################################ MAIN 497 498f_dprintf "%s: Successfully loaded." startup/rcconf.subr 499 500fi # ! $_STARTUP_RCCONF_SUBR 501