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 (INLUDING, 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 __var_to_set="$1" 110 111 # If the in-memory cached value is available, return it immediately 112 if [ "$_STARTUP_RCCONF_MAP" ]; then 113 if [ "$__var_to_set" ]; then 114 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 115 else 116 echo "$STARTUP_RCCONF_MAP" 117 fi 118 return $SUCCESS 119 fi 120 121 # 122 # create the in-memory cache (potentially from validated on-disk cache) 123 # 124 125 # 126 # Calculate digest used to determine if the on-disk global persistant 127 # cache file (containing this digest on the first line) is valid and 128 # can be used to quickly populate the cache value for immediate return. 129 # 130 local __rc_defaults_digest 131 __rc_defaults_digest=$( md5 < "$RC_DEFAULTS" ) 132 133 # 134 # Check to see if the global persistant cache file exists 135 # 136 if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then 137 # 138 # Attempt to populate the in-memory cache with the (soon to be) 139 # validated on-disk cache. If validation fails, fall-back to 140 # the current value and provide error exit status. 141 # 142 STARTUP_RCCONF_MAP=$( 143 ( # Get digest as the first word on the first line 144 read digest rest_ignored 145 146 # 147 # If the stored digest matches the calculated- 148 # one populate the in-memory cache from the on- 149 # disk cache and provide success exit status. 150 # 151 if [ "$digest" = "$__rc_defaults_digest" ] 152 then 153 cat 154 exit $SUCCESS 155 else 156 # Otherwise, return the current value 157 echo "$STARTUP_RCCONF_MAP" 158 exit $FAILURE 159 fi 160 ) < "$STARTUP_RCCONF_MAP_CACHEFILE" 161 ) 162 local __retval=$? 163 export STARTUP_RCCONF_MAP # Make children faster (export cache) 164 if [ $__retval -eq $SUCCESS ]; then 165 export _STARTUP_RCCONF_MAP=1 166 if [ "$__var_to_set" ]; then 167 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 168 else 169 echo "$STARTUP_RCCONF_MAP" 170 fi 171 return $SUCCESS 172 fi 173 # Otherwise, fall-thru to create in-memory cache from scratch 174 fi 175 176 # 177 # If we reach this point, we need to generate the data from scratch 178 # (and after we do, we'll attempt to create the global persistant 179 # cache file to speed up future executions). 180 # 181 182 STARTUP_RCCONF_MAP=$( 183 f_clean_env --except \ 184 PATH \ 185 RC_DEFAULTS \ 186 STARTUP_RCCONF_REGEX \ 187 f_sysrc_desc_awk 188 . "$RC_DEFAULTS" 189 190 # Unset variables we don't want reported 191 unset source_rc_confs_defined 192 193 for var in $( set | awk -F= " 194 function test_print(var) 195 { 196 if ( var == \"OPTIND\" ) return 197 if ( var == \"PATH\" ) return 198 if ( var == \"RC_DEFAULTS\" ) return 199 if ( var == \"STARTUP_RCCONF_REGEX\" ) return 200 if ( var == \"f_sysrc_desc_awk\" ) return 201 print var 202 } 203 /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } 204 " ); do 205 echo $var "$( f_sysrc_desc $var )" 206 done 207 ) 208 export STARTUP_RCCONF_MAP 209 export _STARTUP_RCCONF_MAP=1 210 if [ "$__var_to_set" ]; then 211 setvar "$__var_to_set" "$STARTUP_RCCONF_MAP" 212 else 213 echo "$STARTUP_RCCONF_MAP" 214 fi 215 216 # 217 # Attempt to create the persistant global cache 218 # 219 220 # Create a new temporary file to write to 221 local __tmpfile="$( mktemp -t "$pgm" )" 222 [ "$__tmpfile" ] || return $FAILURE 223 224 # Write the temporary file contents 225 echo "$__rc_defaults_digest" > "$__tmpfile" 226 echo "$STARTUP_RCCONF_MAP" >> "$__tmpfile" 227 228 # Finally, move the temporary file into place 229 case "$STARTUP_RCCONF_MAP_CACHEFILE" in 230 */*) f_quietly mkdir -p "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" 231 esac 232 mv "$__tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" 233} 234 235# f_startup_rcconf_map_expand $var_to_get 236# 237# Expands the map ($var_to_get) into the shell environment namespace by 238# creating _${var}_desc variables containing the description of each variable 239# encountered. 240# 241# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. 242# 243f_startup_rcconf_map_expand() 244{ 245 local var_to_get="$1" 246 eval "$( f_getvar "$var_to_get" | awk ' 247 BEGIN { 248 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 249 } 250 { 251 var = $1 252 desc = $0 253 sub(rword, "", desc) 254 gsub(/'\''/, "'\''\\'\'\''", desc) 255 printf "_%s_desc='\''%s'\''\n", var, desc 256 printf "export _%s_desc\n", var 257 }' )" 258} 259 260# f_dialog_input_view_details 261# 262# Display a menu for selecting which details are to be displayed. The following 263# variables are tracked/modified by the menu/user's selection: 264# 265# SHOW_DESC Show or hide descriptions 266# 267# Mutually exclusive options: 268# 269# SHOW_VALUE Show the value (default; override only) 270# SHOW_DEFAULT_VALUE Show both value and default 271# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in 272# 273# Each variable is treated as a boolean (NULL for false, non-NULL for true). 274# 275# Variables are exported for later-required awk(1) ENVIRON visibility. Returns 276# success unless the user chose `Cancel' or pressed Escape. 277# 278f_dialog_input_view_details() 279{ 280 local prompt= 281 local menu_list # calculated below 282 local defaultitem= # calculated below 283 local hline="$hline_arrows_tab_enter" 284 285 # Calculate marks for checkboxes and radio buttons 286 local md=" " 287 if [ "$SHOW_DESC" ]; then 288 md="X" 289 fi 290 local m1=" " m2=" " m3=" " 291 if [ "$SHOW_VALUE" ]; then 292 m1="*" 293 defaultitem="1 ($m1) $msg_show_value" 294 elif [ "$SHOW_DEFAULT_VALUE" ]; then 295 m2="*" 296 defaultitem="2 ($m2) $msg_show_default_value" 297 elif [ "$SHOW_CONFIGURED" ]; then 298 m3="*" 299 defaultitem="3 ($m3) $msg_show_configured" 300 fi 301 302 # Create the menu list with the above-calculated marks 303 menu_list=" 304 'R $msg_reset' '$msg_reset_desc' 305 'D [$md] $msg_desc' '$msg_desc_desc' 306 '1 ($m1) $msg_show_value' '$msg_show_value_desc' 307 '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' 308 '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' 309 " # END-QUOTE 310 311 local height width rows 312 eval f_dialog_menu_size height width rows \ 313 \"\$DIALOG_TITLE\" \ 314 \"\$DIALOG_BACKTITLE\" \ 315 \"\$prompt\" \ 316 \"\$hline\" \ 317 $menu_list 318 319 f_dialog_title "$msg_choose_view_details" 320 321 local mtag 322 mtag=$( eval $DIALOG \ 323 --title \"\$DIALOG_TITLE\" \ 324 --backtitle \"\$DIALOG_BACKTITLE\" \ 325 --hline \"\$hline\" \ 326 --ok-label \"\$msg_ok\" \ 327 --cancel-label \"\$msg_cancel\" \ 328 --default-item \"\$defaultitem\" \ 329 --menu \"\$prompt\" \ 330 $height $width $rows \ 331 $menu_list \ 332 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 333 ) 334 local retval=$? 335 f_dialog_data_sanitize mtag 336 337 f_dialog_title_restore 338 339 [ $retval -eq 0 ] || return $FAILURE 340 341 case "$mtag" in 342 "R $msg_reset") 343 SHOW_VALUE=1 344 SHOW_DESC=1 345 SHOW_DEFAULT_VALUE= 346 SHOW_CONFIGURED= 347 ;; 348 "D [X] $msg_desc") SHOW_DESC= ;; 349 "D [ ] $msg_desc") SHOW_DESC=1 ;; 350 "1 ("?") $msg_show_value") 351 SHOW_VALUE=1 352 SHOW_DEFAULT_VALUE= 353 SHOW_CONFIGURED= 354 ;; 355 "2 ("?") $msg_show_default_value") 356 SHOW_VALUE= 357 SHOW_DEFAULT_VALUE=1 358 SHOW_CONFIGURED= 359 ;; 360 "3 ("?") $msg_show_configured") 361 SHOW_VALUE= 362 SHOW_DEFAULT_VALUE= 363 SHOW_CONFIGURED=1 364 ;; 365 esac 366} 367 368# f_dialog_input_rclist [$default] 369# 370# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This 371# function should be treated like a call to dialog(1) (the exit status should 372# be captured and f_dialog_menutag_fetch() should be used to get the user's 373# response). Optionally if present and non-null, highlight $default rcvar. 374# 375f_dialog_input_rclist() 376{ 377 local prompt="$msg_please_select_an_rcconf_directive" 378 local menu_list=" 379 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_this_menu'} 380 " # END-QUOTE 381 local defaultitem="$1" 382 local hline="$hline_arrows_tab_enter" 383 384 if [ ! "$_RCCONF_MAP" ]; then 385 # Generate RCCONF_MAP of `var desc ...' per-line 386 f_dialog_info "$msg_creating_rcconf_map" 387 RCCONF_MAP=$( f_startup_rcconf_map ) 388 export RCCONF_MAP 389 # Generate _${var}_desc variables from $RCCONF_MAP 390 f_startup_rcconf_map_expand 391 export _RCCONF_MAP=1 392 fi 393 394 menu_list="$menu_list $( 395 export SHOW_DESC 396 echo "$RCCONF_MAP" | awk ' 397 BEGIN { 398 prefix = "" 399 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 400 } 401 { 402 cur_prefix = tolower(substr($1, 1, 1)) 403 printf "'\''" 404 if ( prefix != cur_prefix ) 405 prefix = cur_prefix 406 else 407 printf " " 408 rcvar = $1 409 printf "%s'\'' '\'\''", rcvar 410 if ( ENVIRON["SHOW_DESC"] ) { 411 desc = $0 412 sub(rword, "", desc) 413 gsub(/'\''/, "'\''\\'\'\''", desc) 414 printf " '\''%s'\''", desc 415 } 416 printf "\n" 417 }' 418 )" 419 420 set -f # set noglob because descriptions in the $menu_list may contain 421 # `*' and get expanded by dialog(1) (doesn't affect Xdialog(1)). 422 # This prevents dialog(1) from expanding wildcards in help line. 423 424 local height width rows 425 eval f_dialog_menu${SHOW_DESC:+_with_help}_size \ 426 height width rows \ 427 \"\$DIALOG_TITLE\" \ 428 \"\$DIALOG_BACKTITLE\" \ 429 \"\$prompt\" \ 430 \"\$hline\" \ 431 $menu_list 432 433 local menu_choice 434 menu_choice=$( eval $DIALOG \ 435 --title \"\$DIALOG_TITLE\" \ 436 --backtitle \"\$DIALOG_BACKTITLE\" \ 437 --hline \"\$hline\" \ 438 --default-item \"\$defaultitem\" \ 439 --ok-label \"\$msg_ok\" \ 440 --cancel-label \"\$msg_cancel\" \ 441 ${SHOW_DESC:+--item-help} \ 442 --menu \"\$prompt\" \ 443 $height $width $rows \ 444 $menu_list \ 445 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 446 ) 447 local retval=$? 448 f_dialog_menutag_store -s "$menu_choice" 449 return $retval 450} 451 452# f_dialog_input_rcvar [$init] 453# 454# Allows the user to enter the name for a new rc.conf(5) variable. If the user 455# does not cancel or press ESC, the $rcvar variable will hold the newly- 456# configured value upon return. 457# 458f_dialog_input_rcvar() 459{ 460 # 461 # Loop until the user provides taint-free/valid input 462 # 463 local _input="$1" 464 while :; do 465 466 # Return if user either pressed ESC or chosen Cancel/No 467 f_dialog_input _input "$msg_please_enter_rcvar_name" \ 468 "$_input" "$hline_alnum_tab_enter" || return 469 470 # Check for invalid entry (1of2) 471 if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then 472 f_dialog_msgbox "$msg_rcvar_must_start_with" 473 continue 474 fi 475 476 # Check for invalid entry (2of2) 477 if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" 478 then 479 f_dialog_msgbox "$msg_rcvar_contains_invalid_chars" 480 continue 481 fi 482 483 rcvar="$_input" 484 break 485 done 486 487 f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" 488 489 return $SUCCESS 490} 491 492############################################################ MAIN 493 494f_dprintf "%s: Successfully loaded." startup/rcconf.subr 495 496fi # ! $_STARTUP_RCCONF_SUBR 497