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