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# 28############################################################ INCLUDES 29 30BSDCFG_SHARE="/usr/share/bsdconfig" 31. $BSDCFG_SHARE/common.subr || exit 1 32f_dprintf "%s: loading includes..." startup/rcconf.subr 33f_include $BSDCFG_SHARE/sysrc.subr 34 35BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" 36f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr 37 38############################################################ GLOBALS 39 40# 41# Initialize in-memory cache variables 42# 43STARTUP_RCCONF_MAP= 44_STARTUP_RCCONF_MAP= 45 46# 47# Define what a variable looks like 48# 49STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" 50 51# 52# Default path to on-disk cache file(s) 53# 54STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" 55 56############################################################ FUNCTIONS 57 58# f_startup_rcconf_list 59# 60# Produce a list of non-default configuration variables configured in the 61# rc.conf(5) collection of files. 62# 63f_startup_rcconf_list() 64{ 65 ( # Operate within a sub-shell to protect the parent environment 66 . "$RC_DEFAULTS" > /dev/null 67 f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files 68 source_rc_confs > /dev/null 69 export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" 70 export RC_DEFAULTS 71 set | awk -F= " 72 function test_print(var) 73 { 74 if ( var == \"OPTIND\" ) return 75 if ( var == \"PATH\" ) return 76 if ( var == \"RC_DEFAULTS\" ) return 77 if ( var == \"STARTUP_RCCONF_REGEX\" ) return 78 if ( var == \"_rc_conf_files_file\" ) return 79 if ( var == \"rc_conf_files\" ) 80 { 81 if ( ENVIRON[\"_rc_conf_files_file\"] == \ 82 ENVIRON[\"RC_DEFAULTS\"] ) return 83 } 84 print var 85 } 86 /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" 87 ) 88} 89 90# f_startup_rcconf_map [$var_to_set] 91# 92# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) 93# variables and their descriptions. The map returned has the following format: 94# 95# var description 96# 97# With each as follows: 98# 99# var the rc.conf(5) variable 100# description description of the variable 101# 102# If $var_to_set is missing or NULL, the map is printed to standard output for 103# capturing in a sub-shell (which is less-recommended because of performance 104# degredation; for example, when called in a loop). 105# 106f_startup_rcconf_map() 107{ 108 local __funcname=f_startup_rcconf_map 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 persistent 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=$( exec 2> /dev/null; md5 < "$RC_DEFAULTS" ) 132 133 # 134 # Check to see if the global persistent 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 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 persistent 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 persistent global cache 218 # 219 220 # Create a new temporary file to write to 221 local __tmpfile 222 f_eval_catch -dk __tmpfile $__funcname mktemp \ 223 'mktemp -t "%s"' "$pgm" || return $FAILURE 224 225 # Write the temporary file contents 226 echo "$__rc_defaults_digest" > "$__tmpfile" 227 echo "$STARTUP_RCCONF_MAP" >> "$__tmpfile" 228 229 # Finally, move the temporary file into place 230 case "$STARTUP_RCCONF_MAP_CACHEFILE" in 231 */*) f_eval_catch -d $__funcname mkdir \ 232 'mkdir -p "%s"' "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" 233 esac 234 f_eval_catch -d $__funcname mv \ 235 'mv "%s" "%s"' "$__tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" 236} 237 238# f_startup_rcconf_map_expand $var_to_get 239# 240# Expands the map ($var_to_get) into the shell environment namespace by 241# creating _${var}_desc variables containing the description of each variable 242# encountered. 243# 244# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. 245# 246f_startup_rcconf_map_expand() 247{ 248 local var_to_get="$1" 249 eval "$( debug= f_getvar "$var_to_get" | awk ' 250 BEGIN { 251 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 252 } 253 { 254 var = $1 255 desc = $0 256 sub(rword, "", desc) 257 gsub(/'\''/, "'\''\\'\'\''", desc) 258 printf "_%s_desc='\''%s'\''\n", var, desc 259 printf "export _%s_desc\n", var 260 }' )" 261} 262 263# f_dialog_input_view_details 264# 265# Display a menu for selecting which details are to be displayed. The following 266# variables are tracked/modified by the menu/user's selection: 267# 268# SHOW_DESC Show or hide descriptions 269# 270# Mutually exclusive options: 271# 272# SHOW_VALUE Show the value (default; override only) 273# SHOW_DEFAULT_VALUE Show both value and default 274# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in 275# 276# Each variable is treated as a boolean (NULL for false, non-NULL for true). 277# 278# Variables are exported for later-required awk(1) ENVIRON visibility. Returns 279# success unless the user chose `Cancel' or pressed Escape. 280# 281f_dialog_input_view_details() 282{ 283 local prompt= 284 local menu_list # calculated below 285 local defaultitem= # calculated below 286 local hline="$hline_arrows_tab_enter" 287 288 # Calculate marks for checkboxes and radio buttons 289 local md=" " 290 if [ "$SHOW_DESC" ]; then 291 md="X" 292 fi 293 local m1=" " m2=" " m3=" " 294 if [ "$SHOW_VALUE" ]; then 295 m1="*" 296 defaultitem="1 ($m1) $msg_show_value" 297 elif [ "$SHOW_DEFAULT_VALUE" ]; then 298 m2="*" 299 defaultitem="2 ($m2) $msg_show_default_value" 300 elif [ "$SHOW_CONFIGURED" ]; then 301 m3="*" 302 defaultitem="3 ($m3) $msg_show_configured" 303 fi 304 305 # Create the menu list with the above-calculated marks 306 menu_list=" 307 'R $msg_reset' '$msg_reset_desc' 308 'D [$md] $msg_desc' '$msg_desc_desc' 309 '1 ($m1) $msg_show_value' '$msg_show_value_desc' 310 '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' 311 '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' 312 " # END-QUOTE 313 314 local height width rows 315 eval f_dialog_menu_size height width rows \ 316 \"\$DIALOG_TITLE\" \ 317 \"\$DIALOG_BACKTITLE\" \ 318 \"\$prompt\" \ 319 \"\$hline\" \ 320 $menu_list 321 322 f_dialog_title "$msg_choose_view_details" 323 324 local mtag 325 mtag=$( eval $DIALOG \ 326 --title \"\$DIALOG_TITLE\" \ 327 --backtitle \"\$DIALOG_BACKTITLE\" \ 328 --hline \"\$hline\" \ 329 --ok-label \"\$msg_ok\" \ 330 --cancel-label \"\$msg_cancel\" \ 331 --default-item \"\$defaultitem\" \ 332 --menu \"\$prompt\" \ 333 $height $width $rows \ 334 $menu_list \ 335 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 336 ) 337 local retval=$? 338 f_dialog_data_sanitize mtag 339 340 f_dialog_title_restore 341 342 [ $retval -eq $DIALOG_OK ] || return $DIALOG_CANCEL 343 344 case "$mtag" in 345 "R $msg_reset") 346 SHOW_VALUE=1 347 SHOW_DESC=1 348 SHOW_DEFAULT_VALUE= 349 SHOW_CONFIGURED= 350 ;; 351 "D [X] $msg_desc") SHOW_DESC= ;; 352 "D [ ] $msg_desc") SHOW_DESC=1 ;; 353 "1 ("?") $msg_show_value") 354 SHOW_VALUE=1 355 SHOW_DEFAULT_VALUE= 356 SHOW_CONFIGURED= 357 ;; 358 "2 ("?") $msg_show_default_value") 359 SHOW_VALUE= 360 SHOW_DEFAULT_VALUE=1 361 SHOW_CONFIGURED= 362 ;; 363 "3 ("?") $msg_show_configured") 364 SHOW_VALUE= 365 SHOW_DEFAULT_VALUE= 366 SHOW_CONFIGURED=1 367 ;; 368 esac 369} 370 371# f_dialog_input_rclist [$default] 372# 373# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This 374# function should be treated like a call to dialog(1) (the exit status should 375# be captured and f_dialog_menutag_fetch() should be used to get the user's 376# response). Optionally if present and non-null, highlight $default rcvar. 377# 378f_dialog_input_rclist() 379{ 380 local prompt="$msg_please_select_an_rcconf_directive" 381 local menu_list=" 382 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_this_menu'} 383 " # END-QUOTE 384 local defaultitem="$1" 385 local hline="$hline_arrows_tab_enter" 386 387 if [ ! "$_RCCONF_MAP" ]; then 388 # Generate RCCONF_MAP of `var desc ...' per-line 389 f_dialog_info "$msg_creating_rcconf_map" 390 RCCONF_MAP=$( f_startup_rcconf_map ) 391 export RCCONF_MAP 392 # Generate _${var}_desc variables from $RCCONF_MAP 393 f_startup_rcconf_map_expand 394 export _RCCONF_MAP=1 395 fi 396 397 menu_list="$menu_list $( 398 export SHOW_DESC 399 echo "$RCCONF_MAP" | awk ' 400 BEGIN { 401 prefix = "" 402 rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" 403 } 404 { 405 cur_prefix = tolower(substr($1, 1, 1)) 406 printf "'\''" 407 if ( prefix != cur_prefix ) 408 prefix = cur_prefix 409 else 410 printf " " 411 rcvar = $1 412 printf "%s'\'' '\'\''", rcvar 413 if ( ENVIRON["SHOW_DESC"] ) { 414 desc = $0 415 sub(rword, "", desc) 416 gsub(/'\''/, "'\''\\'\'\''", desc) 417 printf " '\''%s'\''", desc 418 } 419 printf "\n" 420 }' 421 )" 422 423 set -f # set noglob because descriptions in the $menu_list may contain 424 # `*' and get expanded by dialog(1) (doesn't affect Xdialog(1)). 425 # This prevents dialog(1) from expanding wildcards in help line. 426 427 local height width rows 428 eval f_dialog_menu${SHOW_DESC:+_with_help}_size \ 429 height width rows \ 430 \"\$DIALOG_TITLE\" \ 431 \"\$DIALOG_BACKTITLE\" \ 432 \"\$prompt\" \ 433 \"\$hline\" \ 434 $menu_list 435 436 local menu_choice 437 menu_choice=$( eval $DIALOG \ 438 --title \"\$DIALOG_TITLE\" \ 439 --backtitle \"\$DIALOG_BACKTITLE\" \ 440 --hline \"\$hline\" \ 441 --default-item \"\$defaultitem\" \ 442 --ok-label \"\$msg_ok\" \ 443 --cancel-label \"\$msg_cancel\" \ 444 ${SHOW_DESC:+--item-help} \ 445 --menu \"\$prompt\" \ 446 $height $width $rows \ 447 $menu_list \ 448 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 449 ) 450 local retval=$? 451 f_dialog_menutag_store -s "$menu_choice" 452 return $retval 453} 454 455# f_dialog_input_rcvar [$init] 456# 457# Allows the user to enter the name for a new rc.conf(5) variable. If the user 458# does not cancel or press ESC, the $rcvar variable will hold the newly- 459# configured value upon return. 460# 461f_dialog_input_rcvar() 462{ 463 # 464 # Loop until the user provides taint-free/valid input 465 # 466 local _input="$1" 467 while :; do 468 469 # Return if user either pressed ESC or chosen Cancel/No 470 f_dialog_input _input "$msg_please_enter_rcvar_name" \ 471 "$_input" "$hline_alnum_tab_enter" || return $? 472 473 # Check for invalid entry (1of2) 474 if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then 475 f_show_msg "$msg_rcvar_must_start_with" 476 continue 477 fi 478 479 # Check for invalid entry (2of2) 480 if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" 481 then 482 f_show_msg "$msg_rcvar_contains_invalid_chars" 483 continue 484 fi 485 486 rcvar="$_input" 487 break 488 done 489 490 f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" 491 492 return $DIALOG_OK 493} 494 495############################################################ MAIN 496 497f_dprintf "%s: Successfully loaded." startup/rcconf.subr 498 499fi # ! $_STARTUP_RCCONF_SUBR 500