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