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