packaging: release out (fix TIVI-2652)
[platform/adaptation/setup-scripts.git] / setup-gummiboot-conf
1 #!/bin/sh -euf
2
3 # Copyright 2013-2014 Intel Corporation
4 # Author: Artem Bityutskiy
5 # License: GPLv2
6
7 PROG="setup-gummiboot-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 gummiboot configuration directory
32         conf_dir="$bootdir/loader"
33         # The gummiboot configuration file
34         conf_file="$conf_dir/loader.conf"
35         # The gummiboot kernel entries directory
36         entries_dir="$bootdir/loader/entries"
37 }
38
39 # Get a regular expression for matching gummiboot configuration file option
40 # "$1"
41 get_regexp()
42 {
43         local opt="$(esc_regexp "$1")"
44
45         opt="$(case_insensitive_regexp "$opt")"
46         printf "%s" "\(^[[:blank:]]*$opt[[:blank:]]\+\)\([^[:blank:]]\+\)\([[:blank:]]*$\)"
47 }
48
49 # Create the default gummiboot configuration file.
50 create_default_conf_file()
51 {
52         verbose "creating the default configuration file \"$conf_file\""
53
54         mkdir -p $verbose -- "$conf_dir" >&2
55         cat > "$conf_file" <<-EOF
56         # Generated by $PROG
57         timeout 0
58         default $1
59         EOF
60 }
61
62 # Check wheter the gummiboot configuration file exist, and if not:
63 #   o create the default one if --force option was specified
64 #   o fail if no --force option was specified
65 check_and_create_default_conf_file()
66 {
67         if [ -s "$conf_file" ]; then
68                 return 0
69         fi
70
71         if [ -n "$force" ]; then
72                 create_default_conf_file "$1"
73         else
74                 fatal "cannot find the gummiboot configuration file" \
75                       "(\"$conf_file\") (use -f to create the default" \
76                       "one)"
77         fi
78 }
79
80 #
81 # -----------------------------------------------------------------------------
82 # The "add" subcommand
83 # -----------------------------------------------------------------------------
84 #
85
86 show_add_usage()
87 {
88         cat <<-EOF
89 Usage: $PROG add [options] <entry> <title> <kernel> <options>
90
91 Add a new gummiboot entry. The mandatory arguments are:
92
93     <entry>   - name of the gummiboot entry to add
94                 (<bootdir>/loader/entries/<entry>.conf)
95     <title>   - the title of the entry
96     <kernel>  - name of the kernel binary to add the entry for
97                 (<bootdir>/<kernel>)
98     <options> - kernel boot options
99
100 Options:
101   -f, --force  if the entry already exists - re-write it, if
102                <bootdir>/loader/loader.conf does not exist - create one,
103                if <bootdir>/<kernel> does not exist - do not fail
104   -s, --splash <file>
105                add a splash field to the entry file. <file> is a
106                mandatory parameter to specify splash image location.
107   -h, --help   show this text and exit
108 EOF
109 }
110
111 show_add_usage_fail()
112 {
113         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
114         show_add_usage >&2
115         exit 1
116 }
117
118 add_subcommand()
119 {
120         if [ "$#" -eq 0  ]; then
121                 show_add_usage
122                 exit 0
123         fi
124
125         local tmp
126         tmp=`getopt -n $PROG -o f,s:,h --long force,splash:,help -- "$@"` ||
127                 show_add_usage_fail "cannot parse command-line options"
128         eval set -- "$tmp"
129
130         local force=
131         local splash=
132         while true; do
133                 case "$1" in
134                 -f|--force)
135                         force="-f"
136                         ;;
137                 -s|--splash)
138                         splash="$2"; shift
139                         ;;
140                 -h|--help)
141                         show_add_usage
142                         exit 0
143                         ;;
144                 --) shift; break
145                         ;;
146                 *) show_add_usage_fail "unrecognized option \"$1\""
147                         ;;
148                 esac
149                 shift
150         done
151
152         if [ "$#" -lt 4 ]; then
153                 show_add_usage_fail "too little arguments"
154         fi
155         if [ "$#" -gt 4 ]; then
156                 show_add_usage_fail "too many arguments: \"$1\""
157         fi
158         if [ -n "$splash" ] && ! [ -f "$splash" ]; then
159                 show_add_usage_fail "splash file not found: \"$splash\""
160         fi
161
162         prepare
163
164         local entry="$1"; shift
165         local title="$1"; shift
166         local kernel="$1"; shift
167         local options="$1"; shift
168         local kernel_path="$bootdir/$kernel"
169         local entry_path="$entries_dir/${entry}.conf"
170
171         verbose "entry is \"$entry\""
172         verbose "title is \"$title\""
173         verbose "kernel is \"$kernel\""
174         verbose "options are \"$options\""
175
176         if [ -f "$entry_path" ] && [ -z "$force" ]; then
177                 fatal "gummiboot entry \"$entry_path\" already exists" \
178                       "(use -f to force re-creating it)"
179         fi
180
181         if ! [ -f "$kernel_path" ] && [ -z "$force" ]; then
182                 fatal "cannot find kernel \"$kernel_path\"" \
183                       "(use -f to ignore this error)"
184         fi
185
186         # Make sure the gummiboot configuration file exists
187         check_and_create_default_conf_file "$entry"
188
189         # Find out the kernel version from its name
190         local kernel_version="$(printf "%s" "$kernel" | LC_ALL=C \
191                    sed -e 's/[^[:digit:]]\+-\([[:digit:]]\+.*\)/\1/')"
192         [ -n "$kernel_version" ] || \
193                 fatal "cannot fetch kernel version from \"$kernel\""
194
195         # Create the new entry
196         mkdir -p $verbose -- "$entries_dir" >&2
197         cat > "$entry_path" <<-EOF
198         # Generated by $PROG
199         title $title
200         version $kernel_version
201         efi /$kernel
202         options $options
203         EOF
204
205         # Add an optional splash image entry if the image file exists
206         if [ -n "$splash" ]; then
207                 cp $verbose -- "$splash" "$entries_dir"
208                 printf "%s\n" "splash \\loader\\entries\\${splash##*/}" \
209                 >> "$entry_path"
210         fi
211
212         if [ -n "$verbose" ]; then
213                 verbose "contents of \"$entry_path\":"
214                 cat -- "$entry_path" >&2
215         fi
216 }
217
218 #
219 # -----------------------------------------------------------------------------
220 # The "remove" subcommand
221 # -----------------------------------------------------------------------------
222 #
223
224 show_remove_usage()
225 {
226         cat <<-EOF
227 Usage: $PROG remove [options] <entry>
228
229 Delete gummiboot entry <entry> (<bootdir>/loader/entries/<entry>.conf).
230
231 Options:
232   -f, --force  do not fail if the entry doesn't exist
233   -h, --help   show this text and exit
234 EOF
235 }
236
237 show_remove_usage_fail()
238 {
239         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
240         show_remove_usage >&2
241         exit 1
242 }
243
244 remove_subcommand()
245 {
246         if [ "$#" -eq 0  ]; then
247                 show_remove_usage
248                 exit 0
249         fi
250
251         local tmp
252         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
253                 show_remove_usage_fail "cannot parse command-line options"
254         eval set -- "$tmp"
255
256         local force=
257         while true; do
258                 case "$1" in
259                 -f|--force)
260                         force="-f"
261                         ;;
262                 -h|--help)
263                         show_remove_usage
264                         exit 0
265                         ;;
266                 --) shift; break
267                         ;;
268                 *) show_remove_usage_fail "unrecognized option \"$1\""
269                         ;;
270                 esac
271                 shift
272         done
273
274         if [ "$#" -lt 1 ]; then
275                 show_remove_usage_fail "too little arguments"
276         fi
277         if [ "$#" -gt 1 ]; then
278                 show_remove_usage_fail "too many arguments: \"$1\""
279         fi
280
281         prepare
282
283         local entry="$1"
284         local entry_path="$entries_dir/${entry}.conf"
285
286         if ! [ -f "$entry_path" ] && [ -z "$force" ]; then
287                 fatal "gummiboot entry \"$entry_path\" doesn't exist" \
288                       "(use -f to ignore this error)"
289         fi
290
291         rm -rf $verbose -- "$entry_path" >&2
292         verbose "removed $entry_path"
293 }
294
295 #
296 # -----------------------------------------------------------------------------
297 # The "default" subcommand
298 # -----------------------------------------------------------------------------
299 #
300
301 # Get the kernel name from a gummiboot entry
302 get_kernel_from_entry()
303 {
304         local result=
305         local entry="$entries_dir/$1.conf"
306
307         if [ -f "$entry" ]; then
308                 local regexp="$(get_regexp "efi")"
309                 local result="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
310                                 "$entry")"
311
312                 if [ -z "$result" ]; then
313                         regexp="$(get_regexp "linux")"
314                         result="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
315                                   "$entry")"
316
317                         [ -n "$result" ] || return 0
318                 fi
319         fi
320
321         printf "%s" "${result##*/}"
322 }
323
324 show_default_usage()
325 {
326         cat <<-EOF
327 Usage: $PROG default [options] <entry>
328
329 Set the default boot kernel to <entry>, which is the gummiboot entry name to
330 become the default (without the ".conf" suffix, like in
331 <bootdir>/loader/entries/<entry>.conf). If <entry> is omited, this command
332 prints the currently default entry name.
333
334 Options:
335   -f, --force  if <bootdir>/loader/loader.conf does not exist - create the
336                default one, if <bootdir>/loader/entries/<entry>.conf does not
337                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 entry="${1:-}";
381
382         # Make sure the gummiboot configuration file exists
383         check_and_create_default_conf_file "$entry"
384
385         # Find the current default entry
386         local regexp="$(get_regexp "default")"
387         local default_entry="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
388                                 "$conf_file")"
389
390         if [ -z "$entry" ]; then
391                 printf "%s\n" "entry: $default_entry"
392                 printf "%s\n" "kernel: $(get_kernel_from_entry "$default_entry")"
393                 return 0
394         fi
395
396         local entry_path="$entries_dir/${entry}.conf"
397         if ! [ -f "$entry_path" ] && [ -z "$force" ]; then
398                 fatal "cannot find the gummiboot entry \"$entry_path\"" \
399                       "(use -f to ignore this error)"
400         fi
401
402         if [ -z "$default_entry" ]; then
403                 verbose "no default entry found, adding \"$entry\""
404                 printf "%s" "default $entry" >> "$conf_file"
405                 return 0
406         fi
407
408         [ "$(printf "%s\n" "$default_entry" | wc -l)" -eq "1" ] || \
409                 fatal "more than one default entry in \"$conf_file\""
410
411         # Escape special sed characters in "$entry" and replace the old default
412         # entry with the new one
413         local entry_esc="$(esc_sed_replacement "$entry")"
414         LC_ALL=C sed -i -e "s/$regexp/\1$entry_esc\3/" -- "$conf_file"
415
416         verbose "set the default boot kernel to \"$entry"\"
417 }
418
419 #
420 # -----------------------------------------------------------------------------
421 #
422
423 show_usage()
424 {
425         cat <<-EOF
426 Usage: $PROG [options] <subcommand> [options] <arguments>
427
428 This program changes the gummiboot bootloader configuration. Supported
429 subcommands are:
430    add     - add a gummiboot entry for a kernel
431    remove  - remove a gummiboot entry
432    default - get or set the default gummiboot entry
433
434 Run "$PROG <subcommand>" to see subcommand-specific help.
435
436 Options:
437   -b, --bootdir  path to the boot directory (default is "/boot")
438   --version      show the program version and exit
439   -v, --verbose  be verbose
440   -h, --help     show this text and exit
441 EOF
442 }
443
444 show_usage_fail()
445 {
446         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
447         show_usage >&2
448         exit 1
449 }
450
451 bootdir="/boot"
452 verbose=
453 while [ -n "${1:-""}" ] && [ -z "${1##-*}" ]; do
454         case "$1" in
455         -b|--bootdir)
456                 shift
457                 # If there is no argument or it starts with "-", complain
458                 if [ -z "${1:-""}" ] || [ -z "${1##-*}" ]; then
459                         fatal "--bootdir requires an argument"
460                 fi
461                 bootdir="$1"
462                 ;;
463         --version)
464                 printf "%s\n" "$VER"
465                 exit 0
466                 ;;
467         -v|--verbose)
468                 verbose="-v"
469                 ;;
470         -h|--help)
471                 show_usage
472                 exit 0
473                 ;;
474         --) shift; break
475                 ;;
476         *) show_usage_fail "unrecognized option \"$1\""
477                 ;;
478         esac
479         shift
480 done
481
482 # Parse subcommands
483
484 if [ "$#" -eq 0 ]; then
485         show_usage
486         exit 0
487 fi
488
489
490 subcommand="$1"; shift
491
492 case "$subcommand" in
493 add)
494         add_subcommand "$@"
495         break
496         ;;
497 remove)
498         remove_subcommand "$@"
499         break
500         ;;
501 default)
502         default_subcommand "$@"
503         break
504         ;;
505 *) show_usage_fail "unrecognized subcommand \"$subcommand\""
506         ;;
507 esac