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