setup-gummiboot-conf: add splash file support
[platform/adaptation/setup-scripts.git] / setup-extlinux-conf
1 #!/bin/sh -euf
2
3 # Copyright 2013-2014 Intel Corporation
4 # Author: Artem Bityutskiy
5 # License: GPLv2
6
7 PROG="setup-extlinux-conf"
8 VER="1.0"
9
10 srcdir="$(readlink -ev -- ${0%/*})"
11 if [ -f "$srcdir/setup-ivi-sh-functions" ]; then
12         . "$srcdir/setup-ivi-sh-functions"
13 else
14         .  /usr/share/setup-ivi/setup-ivi-sh-functions
15 fi
16
17 # This is a small trick which I use to make sure my scripts are portable -
18 # check if 'dash' is present, and if yes - use it.
19 if can_switch_to_dash; then
20         exec dash -euf -- "$srcdir/$PROG" "$@"
21         exit $?
22 fi
23
24 # Common preparations for all subcommands.
25 prepare()
26 {
27         verbose "Boot directory is $bootdir"
28         [ -d "$bootdir" ] || \
29                 fatal "boot directory path \"$bootdir\" does not exist"
30
31         # The extlinux configuration directory
32         conf_dir="$bootdir/extlinux"
33         # The extlinux configuration file
34         conf_file="$conf_dir/extlinux.conf"
35 }
36
37 # Create the default extlinux configuration file.
38 create_default_conf_file()
39 {
40         verbose "creating the default configuration file \"$conf_file\""
41
42         mkdir -p $verbose -- "$conf_dir" >&2
43         cat > "$conf_file" <<-EOF
44         # Generated by $PROG
45         ui vesamenu.c32
46         prompt 0
47         timeout 1
48         default $1
49         EOF
50 }
51
52 # Check wheter the extlinux configuration file exist and if not:
53 #   o create the default one if --force option was specified
54 #   o fail if no --force option was specified
55 check_and_create_default_conf_file()
56 {
57         if [ -s "$conf_file" ]; then
58                 return 0
59         fi
60
61         if [ -n "$force" ]; then
62                 create_default_conf_file "$1"
63         else
64                 fatal "cannot find the extlinux configuration file" \
65                       "(\"$conf_file\") (use -f to force creating the" \
66                       "default one)"
67         fi
68 }
69
70 # Get a regular expression for matching extlinux configuration file option "$1"
71 get_regexp()
72 {
73         local opt="$(esc_regexp "$1")"
74
75         opt="$(case_insensitive_regexp "$opt")"
76         printf "%s" "\(^[[:blank:]]*$opt[[:blank:]]\+\)\([^[:blank:]]\+\)\([[:blank:]]*$\)"
77 }
78
79 # Return a regular expression for matching label with name "$1".
80 label_regexp()
81 {
82         local opt="$(case_insensitive_regexp "label")"
83         local label="$(esc_regexp "$1")"
84
85         printf "%s" "\(^[[:blank:]]*$opt[[:blank:]]\+\)\($label\)\([[:blank:]]*$\)"
86 }
87
88 remove_label()
89 {
90         local label="$1"
91         local l_regexp="$(label_regexp "$label")"
92         local anyl_regexp="$(get_regexp "label")"
93
94         LC_ALL=C sed -i -n -e "/$l_regexp/ { # mathes our label line
95                         :l n;                # get the next line
96                         /$l_regexp/bl        # same label again, keep skipping
97                         /$anyl_regexp/!bl    # a different label, stop skipping
98                     }
99                     /$l_regexp/!p            # print all other lines
100                     " -- "$conf_file"
101
102         remove_trailing_empty_lines "$conf_file"
103 }
104
105 #
106 # -----------------------------------------------------------------------------
107 # The "add" subcommand
108 # -----------------------------------------------------------------------------
109 #
110
111 show_add_usage()
112 {
113         cat <<-EOF
114 Usage: $PROG add [options] <label> <title> <kernel> <options>
115
116 Add a new extlinux boot menu entry. The mandatory arguments are:
117
118     <label>   - a unique name of the boot menu entry to add, the entry will be
119                 further refurred by the label
120     <title>   - the boot menu entry title
121     <kernel>  - name of the kernel binary corresponding to the entry
122     <options> - kernel boot options
123
124 Options:
125   -f, --force  if <label> already exists - re-create it, if
126                <bootdir>/extlinux/extlinux.conf does not exist - create it, if
127                <bootdir>/<kernel> does not exist - do not fail
128   -h, --help   show this text and exit
129 EOF
130 }
131
132 show_add_usage_fail()
133 {
134         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
135         show_add_usage >&2
136         exit 1
137 }
138
139 add_subcommand()
140 {
141         if [ "$#" -eq 0  ]; then
142                 show_add_usage
143                 exit 0
144         fi
145
146         local tmp
147         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
148                 show_add_usage_fail "cannot parse command-line options"
149         eval set -- "$tmp"
150
151         local force=
152         while true; do
153                 case "$1" in
154                 -f|--force)
155                         force="-f"
156                         ;;
157                 -h|--help)
158                         show_add_usage
159                         exit 0
160                         ;;
161                 --) shift; break
162                         ;;
163                 *) show_add_usage_fail "unrecognized option \"$1\""
164                         ;;
165                 esac
166                 shift
167         done
168
169         if [ "$#" -lt 4 ]; then
170                 show_add_usage_fail "too little arguments"
171         fi
172         if [ "$#" -gt 4 ]; then
173                 show_add_usage_fail "too many arguments: \"$1\""
174         fi
175
176         prepare
177
178         local label="$1"; shift
179         local title="$1"; shift
180         local kernel="$1"; shift
181         local options="$1"; shift
182         local kernel_path="$bootdir/$kernel"
183
184         verbose "label is \"$label\""
185         verbose "title is \"$title\""
186         verbose "kernel is \"$kernel\""
187         verbose "options are \"$options\""
188
189         if ! [ -f "$kernel_path" ] && [ -z "$force" ]; then
190                 fatal "cannot find kernel \"$kernel_path\"" \
191                       "(use -f to ignore this error)"
192         fi
193
194         # Make sure the extlinux configuration file exists
195         check_and_create_default_conf_file "$label"
196
197         if LC_ALL=C grep -q -e "$(label_regexp "$label")" -- "$conf_file" && \
198            [ -z "$force" ]; then
199                 fatal "extlinux boot menu label \"$label\" already exists" \
200                       "(use -f to force re-creating it)"
201         fi
202
203         # Find out the kernel version from its name
204         local kernel_version="$(printf "%s" "$kernel" | LC_ALL=C \
205                    sed -e 's/[^[:digit:]]\+-\([[:digit:]]\+.*\)/\1/')"
206         [ -n "$kernel_version" ] || \
207                 fatal "cannot fetch kernel version from \"$kernel\""
208
209         # Remove the label if it exists, since we are going to add a new one
210         remove_label "$label"
211
212         local block="label $label
213         menu label $title ($kernel_version)
214         linux /$kernel
215         append $options"
216
217         printf "\n%s\n" "$block" >> "$conf_file"
218
219         if [ -n "$verbose" ]; then
220                 verbose "Added the following to \"$conf_file\":"
221                 printf "%s\n" "$block" >&2
222         fi
223 }
224
225 #
226 # -----------------------------------------------------------------------------
227 # The "remove" subcommand
228 # -----------------------------------------------------------------------------
229 #
230
231 show_remove_usage()
232 {
233         cat <<-EOF
234 Usage: $PROG remove [options] <label>
235
236 Delete extlinux boot entry which has label <label>.
237
238 Options:
239   -f, --force  do not fail if the entry doesn't exist
240   -h, --help   show this text and exit
241 EOF
242 }
243
244 show_remove_usage_fail()
245 {
246         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
247         show_remove_usage >&2
248         exit 1
249 }
250
251 remove_subcommand()
252 {
253         if [ "$#" -eq 0  ]; then
254                 show_remove_usage
255                 exit 0
256         fi
257
258         local tmp
259         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
260                 show_remove_usage_fail "cannot parse command-line options"
261         eval set -- "$tmp"
262
263         local force=
264         while true; do
265                 case "$1" in
266                 -f|--force)
267                         force="-f"
268                         ;;
269                 -h|--help)
270                         show_remove_usage
271                         exit 0
272                         ;;
273                 --) shift; break
274                         ;;
275                 *) show_remove_usage_fail "unrecognized option \"$1\""
276                         ;;
277                 esac
278                 shift
279         done
280
281         if [ "$#" -lt 1 ]; then
282                 show_remove_usage_fail "too little arguments"
283         fi
284         if [ "$#" -gt 1 ]; then
285                 show_remove_usage_fail "too many arguments: \"$1\""
286         fi
287
288         prepare
289
290         local label="$1"
291
292         if ! LC_ALL=C grep -q -e "$(label_regexp "$label")" -- "$conf_file" && \
293            [ -z "$force" ]; then
294                 fatal "cannot find label \"$label\" in \"$conf_file\"" \
295                       "(use -f to ignore this error)"
296         fi
297
298         remove_label "$label"
299         verbose "removed $label"
300 }
301
302 #
303 # -----------------------------------------------------------------------------
304 # The "default" subcommand
305 # -----------------------------------------------------------------------------
306 #
307
308 # Get the kernel binary name by its extlinux.conf label name
309 get_kernel_by_label()
310 {
311         local label="$1"
312         local l_regexp="$(label_regexp "$label")"  # Regexp for our label
313         local anyl_regexp="$(get_regexp "label")"  # Regexp for any label
314         local linux_regexp="$(get_regexp "linux")"
315         local kernel_regexp="$(get_regexp "kernel")"
316         local result
317
318         [ -z "$label" ] && return 0
319
320         result="$(LC_ALL=C sed -n -e "/$l_regexp/ {
321                 :l n;
322                 /$linux_regexp/ { s/$linux_regexp/\2/p }
323                 /$kernel_regexp/ { s/$kernel_regexp/\2/p }
324                 /$anyl_regexp/!bl # Loop till the next label
325         }" -- "$conf_file")"
326
327         printf "%s" "${result##*/}"
328 }
329
330 show_default_usage()
331 {
332         cat <<-EOF
333 Usage: $PROG default [options] <label>
334
335 Set the default boot kernel to be the kernel which is marked with label <label>
336 the extlinux configuration file. If <label> is omited, this command prints the
337 currently default entry name.
338
339 Options:
340   -f, --force  <bootdir>/extlinux/extlinux.conf does not exist - create it, if
341                <label> does not exist - do not fail
342   -h, --help   show this text and exit
343 EOF
344 }
345
346 show_default_usage_fail()
347 {
348         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
349         show_default_usage >&2
350         exit 1
351 }
352
353 default_subcommand()
354 {
355         local tmp
356         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
357                 show_default_usage_fail "cannot parse command-line options"
358         eval set -- "$tmp"
359
360         local force=
361         while true; do
362                 case "$1" in
363                 -f|--force)
364                         force="-f"
365                         ;;
366                 -h|--help)
367                         show_default_usage
368                         exit 0
369                         ;;
370                 --) shift; break
371                         ;;
372                 *) show_default_usage_fail "unrecognized option \"$1\""
373                         ;;
374                 esac
375                 shift
376         done
377
378         if [ "$#" -gt 1 ]; then
379                 show_default_usage_fail "too many arguments: \"$1\""
380         fi
381
382         prepare
383
384         local label="${1:-}";
385
386         # Make sure the extlinux configuration file exists
387         check_and_create_default_conf_file "$label"
388
389         # Find the current default label
390         local regexp="$(get_regexp "default")"
391         local default_label="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
392                                 "$conf_file")"
393
394         if [ -z "$label" ]; then
395                 printf "%s\n" "label: $default_label"
396                 printf "%s\n" "kernel: $(get_kernel_by_label "$default_label")"
397                 return 0
398         fi
399
400         local l_regexp="$(label_regexp "$label")"
401         local labels="$(LC_ALL=C grep -e "$l_regexp" -- "$conf_file" | wc -l)"
402
403         if [ "$labels" -eq "0" ] && [ -z "$force" ]; then
404                 fatal "cannot find label \"$label\" in \"$conf_file\"" \
405                       "(use -f to ignore this error)"
406         fi
407
408         if [ "$labels" -gt "1" ]; then
409                 fatal "more than one labels \"$label\" in \"$conf_file\""
410         fi
411
412         if [ -z "$default_label" ]; then
413                 verbose "no default label found, adding \"$label\""
414
415                 local def="$(esc_sed_replacement "default $label")"
416                 local anyl_regexp="$(get_regexp "label")"
417
418                 LC_ALL=C sed -i -e "
419                         $                 { s/.*/&\n$def/; q }
420                         /^[[:blank:]]*$/  { s/.*/$def\n&/; q }
421                         /$(anyl_regexp)/  { s/.*/$def\n&/; q }
422                         " -- "$conf_file"
423                 return 0
424         fi
425
426         # Escape special sed characters in "$entry" and replace the old default
427         # entry with the new one
428         local esc_label="$(esc_sed_replacement "$label")"
429         LC_ALL=C sed -i -e "s/$regexp/\1$esc_label\3/" -- "$conf_file"
430         verbose "set the default boot kernel to \"$label"\"
431 }
432
433 #
434 # -----------------------------------------------------------------------------
435 #
436
437 show_usage()
438 {
439         cat <<-EOF
440 Usage: $PROG [options] <subcommand> [options] <arguments>
441
442 This program changes the extlinux bootloader configuration. Supported
443 subcommands are:
444    add     - add an extlinux boot menu entry for a kernel
445    remove  - remove an extlinux boot menu entry
446    default - get or set the default extlinux boot menu entry
447
448 Run "$PROG <subcommand>" to see subcommand-specific help.
449
450 Options:
451   -b, --bootdir  path to the boot directory (default is "/boot")
452   --version      show the program version and exit
453   -v, --verbose  be verbose
454   -h, --help     show this text and exit
455 EOF
456 }
457
458 show_usage_fail()
459 {
460         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
461         show_usage >&2
462         exit 1
463 }
464
465 bootdir="/boot"
466 verbose=
467 while [ -n "${1:-""}" ] && [ -z "${1##-*}" ]; do
468         case "$1" in
469         -b|--bootdir)
470                 shift
471                 # If there is no argument or it starts with "-", complain
472                 if [ -z "${1:-""}" ] || [ -z "${1##-*}" ]; then
473                         fatal "--bootdir requires an argument"
474                 fi
475                 bootdir="$1"
476                 ;;
477         --version)
478                 printf "%s\n" "$VER"
479                 exit 0
480                 ;;
481         -v|--verbose)
482                 verbose="-v"
483                 ;;
484         -h|--help)
485                 show_usage
486                 exit 0
487                 ;;
488         --) shift; break
489                 ;;
490         *) show_usage_fail "unrecognized option \"$1\""
491                 ;;
492         esac
493         shift
494 done
495
496 # Parse subcommands
497
498 if [ "$#" -eq 0 ]; then
499         show_usage
500         exit 0
501 fi
502
503
504 subcommand="$1"; shift
505
506 case "$subcommand" in
507 add)
508         add_subcommand "$@"
509         break
510         ;;
511 remove)
512         remove_subcommand "$@"
513         break
514         ;;
515 default)
516         default_subcommand "$@"
517         break
518         ;;
519 *) show_usage_fail "unrecognized subcommand \"$subcommand\""
520         ;;
521 esac