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