]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bsdconfig/bsdconfig
unbound: Import upstream 0ee44ef3 when ENOBUFS is returned
[FreeBSD/FreeBSD.git] / usr.sbin / bsdconfig / bsdconfig
1 #!/bin/sh
2 #-
3 # Copyright (c) 2012 Ron McDowell
4 # Copyright (c) 2012-2021 Devin Teske
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 # SUCH DAMAGE.
27 #
28 #
29 ############################################################ INCLUDES
30
31 # When common.subr is included, it automatically scans "$@" for `-d' and/or
32 # `-D file' arguments to conditionally enable debugging. Similarly, when
33 # dialog.subr is included, it automatically scans "$@" for `-X' and/or `-S'.
34 # To prevent this scanning from becoming confused by extra options, define
35 # any/all extra arguments to use in the optstring to getopts when scanning
36 # for dedicated options such as those described.
37 #
38 # NOTE: This needs to be declared before including `common.subr'.
39 # NOTE: You really only need to list flags that require an argument as unknown
40 #       flags are silently accepted unless they take an argument (in which case
41 #       the following argument will terminate option processing unless it looks
42 #       like a flag).
43 #
44 GETOPTS_EXTRA="f:"
45
46 BSDCFG_SHARE="/usr/share/bsdconfig"
47 . $BSDCFG_SHARE/common.subr || exit 1
48 f_dprintf "%s: loading includes..." "$0"
49 f_include $BSDCFG_SHARE/dialog.subr
50 f_include $BSDCFG_SHARE/mustberoot.subr
51 f_include $BSDCFG_SHARE/strings.subr
52
53 BSDCFG_LIBE="/usr/libexec/bsdconfig"
54 f_include_lang $BSDCFG_LIBE/include/messages.subr
55
56 BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp
57 USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp
58
59 ############################################################ CONFIGURATION
60
61 #
62 # Alternate `local' libexec directory for add-on modules (e.g., from ports)
63 #
64 BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig"
65
66 ############################################################ FUNCTIONS
67
68 # usage
69 #
70 # display usage and exit
71 #
72 usage()
73 {
74         local index="INDEX"
75         local cmd_list # Calculated below
76
77         cd $BSDCFG_LIBE
78                 # No need to preserve CWD (headed toward exit)
79
80         # Test for language-specific indices
81         f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
82                 index="$index.${LANG:-$LC_ALL}"
83
84         cmd_list=$(
85                 awk '/^menu_selection="/ {
86                         sub(/\|.*/, "")
87                         sub(/^menu_selection="/, "")
88                         print
89                 }' */$index | sort
90         )
91
92         local alt_cmd_list # Calculated below (if $BSDCFG_LOCAL_LIBE exists)
93         if f_quietly cd $BSDCFG_LOCAL_LIBE; then
94                 # No need to preserve CWD (headed toward exit)
95
96                 # Test for language-specific indices
97                 f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
98                         index="$index.${LANG:-$LC_ALL}"
99
100                 alt_cmd_list=$(
101                         awk '/^menu_selection="/ {
102                                 sub(/\|.*/, "")
103                                 sub(/^menu_selection="/, "")
104                                 print
105                         }' */$index 2> /dev/null | sort
106                 )
107
108                 # Conflate lists, removing duplicates
109                 cmd_list=$( printf "%s\n%s\n" \
110                                    "$cmd_list" "$alt_cmd_list" | sort -u )
111         fi
112
113         #
114         # Determine the longest command-length (in characters)
115         #
116         local longest_cmd
117         longest_cmd=$( echo "$cmd_list" | f_longest_line_length )
118         f_dprintf "longest_cmd=[%s]" "$longest_cmd"
119
120         #
121         # Determine the maximum width of terminal/console
122         #
123         local max_size="$( stty size 2> /dev/null )"
124         : ${max_size:="24 80"}
125         local max_width="${max_size#*[$IFS]}"
126         f_dprintf "max_width=[%s]" "$max_width"
127
128         #
129         # Using the longest command-length as the width of a single column,
130         # determine if we can use more than one column to display commands.
131         #
132         local x=$longest_cmd ncols=1
133         x=$(( $x + 8 )) # Accommodate leading tab character
134         x=$(( $x + 3 + $longest_cmd )) # Pre-load end of next column
135         while [ $x -lt $max_width ]; do
136                 ncols=$(( $ncols + 1 ))
137                 x=$(( $x + 3 + $longest_cmd ))
138         done
139         f_dprintf "ncols=[%u] x=[%u]" $ncols $x
140
141         #
142         # Re-format the command-list into multiple columns
143         #
144         cmd_list=$( eval "$( echo "$cmd_list" |
145                 awk -v ncols=$ncols -v size=$longest_cmd '
146                 BEGIN {
147                         n = 0
148                         row_item[1] = ""
149                 }
150                 function print_row()
151                 {
152                         fmt = "printf \"\\t%-" size "s"
153                         for (i = 1; i < cur_col; i++)
154                                 fmt = fmt "   %-" size "s"
155                         fmt = fmt "\\n\""
156                         printf "%s", fmt
157                         for (i = 1; i <= cur_col; i++)
158                                 printf " \"%s\"", row_item[i]
159                         print ""
160                 }
161                 {
162                         n++
163                         cur_col = (( n - 1 ) % ncols ) + 1
164                         printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n",
165                                cur_col, $0
166                         row_item[cur_col] = $0
167                         if ( cur_col == ncols ) print_row()
168                 }
169                 END {
170                         if ( cur_col < ncols ) print_row()
171                 }' )"
172         )
173
174         f_usage $BSDCFG_LIBE/USAGE \
175                 "PROGRAM_NAME" "$pgm" \
176                 "COMMAND_LIST" "$cmd_list"
177
178         # NOTREACHED
179 }
180
181 # dialog_menu_main
182 #
183 # Display the dialog(1)-based application main menu.
184 #
185 dialog_menu_main()
186 {
187         local title="$DIALOG_TITLE"
188         local btitle="$DIALOG_BACKTITLE"
189         local prompt="$msg_menu_text"
190         local menu_list="
191                 'X' '$msg_exit'  '$msg_exit_bsdconfig'
192                 '1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system'
193         " # END-QUOTE
194         local defaultitem= # Calculated below
195         local hline=
196
197         #
198         # Pick up the base modules (directories named `[0-9][0-9][0-9].*')
199         #
200         local menuitem menu_title menu_help menu_selection index=2
201         for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do
202                 [ -f "$BSDCFG_LIBE/$menuitem/INDEX" ] || continue
203                 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break
204
205                 menu_program= menu_title= menu_help=
206                 f_include_lang $BSDCFG_LIBE/$menuitem/INDEX
207                 [ "$menu_program" ] || continue
208
209                 case "$menu_program" in
210                 /*) : already fully qualified ;;
211                  *) menu_program="$menuitem/$menu_program"
212                 esac
213
214                 f_substr -v tag "$DIALOG_MENU_TAGS" $index 1
215                 setvar "menu_program$tag" "$menu_program"
216
217                 f_shell_escape "$menu_title" menu_title
218                 f_shell_escape "$menu_help" menu_help
219                 menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"
220
221                 index=$(( $index + 1 ))
222         done
223
224         #
225         # Process the `local' libexec sources.
226         #
227         # Whereas modules in $BSDCFG_LIBE must be named [0-9][0-9][0-9].*
228         # modules in $BSDCFG_LOCAL_LIBE should NOT be named this way (making it
229         # more practical for port-maintainers).
230         #
231         # This also has the fortunate side-effect of making the de-duplication
232         # effort rather simple (because so-called `base' modules must be named
233         # differently than add-on modules).
234         #
235         local separator_added=
236         for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * )
237         do
238                 # Skip the module if it looks like a `base' module
239                 case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac
240
241                 [ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue
242                 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break
243
244                 menu_program= menu_title= menu_help=
245                 f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue
246                 [ "$menu_program" ] || continue
247
248                 if [ ! "$separator_added" ]; then
249                         menu_list="$menu_list '-' '-' ''"
250                         separator_added=1
251                 fi
252
253                 case "$menu_program" in
254                 /*) : already fully qualified ;;
255                  *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program"
256                 esac
257
258                 f_substr -v tag "$DIALOG_MENU_TAGS" $index 1
259                 setvar "menu_program$tag" "$menu_program"
260
261                 f_shell_escape "$menu_title" menu_title
262                 f_shell_escape "$menu_help" menu_help
263                 menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"
264
265                 index=$(( $index + 1 ))
266         done
267
268         local height width rows
269         eval f_dialog_menu_with_help_size height width rows \
270                                           \"\$title\"  \
271                                           \"\$btitle\" \
272                                           \"\$prompt\" \
273                                           \"\$hline\"  \
274                                           $menu_list
275
276         # Obtain default-item from previously stored selection
277         f_dialog_default_fetch defaultitem
278
279         local menu_choice
280         menu_choice=$( eval $DIALOG \
281                 --clear                                 \
282                 --title \"\$title\"                     \
283                 --backtitle \"\$btitle\"                \
284                 --hline \"\$hline\"                     \
285                 --item-help                             \
286                 --ok-label \"\$msg_ok\"                 \
287                 --cancel-label \"\$msg_exit_bsdconfig\" \
288                 --help-button                           \
289                 --help-label \"\$msg_help\"             \
290                 ${USE_XDIALOG:+--help \"\"}             \
291                 --default-item \"\$defaultitem\"        \
292                 --menu \"\$prompt\"                     \
293                 $height $width $rows                    \
294                 $menu_list                              \
295                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
296         )
297         local retval=$?
298         f_dialog_data_sanitize menu_choice
299         f_dialog_menutag_store "$menu_choice"
300
301         # Only update default-item on success
302         [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice"
303
304         return $retval
305 }
306
307 ############################################################ MAIN
308
309 #
310 # If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or
311 # as a valid resword (see script.subr for additional details about reswords).
312 #
313 if [ "$pgm" != "bsdconfig" ]; then
314         if indexfile=$( f_index_file "$pgm" ) &&
315            cmd=$( f_index_menusel_command "$indexfile" "$pgm" )
316         then
317                 f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*"
318                 exec "$cmd" "$@" || exit 1
319         else
320                 f_include $BSDCFG_SHARE/script.subr
321                 for resword in $RESWORDS; do
322                         [ "$pgm" = "$resword" ] || continue
323                         # Found a match
324                         f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
325                         f_dispatch $resword $resword "$@"
326                         exit $?
327                 done
328         fi
329 fi
330
331 #
332 # Process command-line arguments
333 #
334 scripts_loaded=0
335 while getopts f:h$GETOPTS_STDARGS flag; do
336         case "$flag" in
337         f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
338            f_script_load "$OPTARG"
339            scripts_loaded=$(( $scripts_loaded + 1 )) ;;
340         h|\?) usage ;;
341         esac
342 done
343 shift $(( $OPTIND - 1 ))
344
345 # If we've loaded any scripts, do not continue any further
346 [ $scripts_loaded -gt 0 ] && exit
347
348 #
349 # Initialize
350 #
351 f_dialog_title "$msg_main_menu"
352
353 [ "$SECURE" ] && f_mustberoot_init
354
355 # Incorporate rc-file if it exists
356 [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
357
358 #
359 # If a non-option argument was passed, process it as a menuitem selection...
360 #
361 if [ "$1" ]; then
362         #
363         # ...unless it's a long-option for usage.
364         #
365         case "$1" in -help|--help|-\?)
366                 usage
367                 # NOTREACHED
368         esac
369
370         #
371         # Find the INDEX (possibly i18n) claiming this keyword and get the
372         # command to execute from the menu_selection line.
373         #
374         if ! { indexfile=$( f_index_file "$1" ) &&
375                cmd=$( f_index_menusel_command "$indexfile" "$1" )
376         }; then
377                 # No matches, display usage (which shows valid keywords)
378                 f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
379                 usage
380                 # NOTREACHED
381         fi
382
383         f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*"
384         shift
385         exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
386         # NOTREACHED
387 fi
388
389 #
390 # Launch application main menu
391 #
392 while :; do
393         dialog_menu_main
394         retval=$?
395         f_dialog_menutag_fetch mtag
396         f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
397
398         if [ $retval -eq $DIALOG_HELP ]; then
399                 f_show_help "$BSDCONFIG_HELPFILE"
400                 continue
401         elif [ $retval -ne $DIALOG_OK ]; then
402                 f_die
403         fi
404
405         case "$mtag" in
406         X) break ;;
407         1) # Usage
408            f_show_help "$USAGE_HELPFILE"
409            continue
410         esac
411
412         # Anything else is a dynamically loaded menuitem
413
414         f_getvar menu_program$mtag menu_program
415         case "$menu_program" in
416         /*) cmd="$menu_program" ;;
417          *) cmd="$BSDCFG_LIBE/$menu_program"
418         esac
419         f_dprintf "cmd=[%s]" "$cmd"
420         $cmd ${USE_XDIALOG:+-X}
421 done
422
423 exit $SUCCESS
424
425 ################################################################################
426 # END
427 ################################################################################