4fa7fbe34d401fdb052ee0cbabfdb708115748cf
[platform/adaptation/setup-scripts.git] / setup-gummiboot-conf
1 #!/bin/sh -euf
2
3 # Copyright 2013 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   -h, --help   show this text and exit
105 EOF
106 }
107
108 show_add_usage_fail()
109 {
110         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
111         show_add_usage >&2
112         exit 1
113 }
114
115 add_subcommand()
116 {
117         if [ "$#" -eq 0  ]; then
118                 show_add_usage
119                 exit 0
120         fi
121
122         local tmp
123         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
124                 show_add_usage_fail "cannot parse command-line options"
125         eval set -- "$tmp"
126
127         local force=
128         while true; do
129                 case "$1" in
130                 -f|--force)
131                         force="-f"
132                         ;;
133                 -h|--help)
134                         show_add_usage
135                         exit 0
136                         ;;
137                 --) shift; break
138                         ;;
139                 *) show_add_usage_fail "unrecognized option \"$1\""
140                         ;;
141                 esac
142                 shift
143         done
144
145         if [ "$#" -lt 4 ]; then
146                 show_add_usage_fail "too little arguments"
147         fi
148         if [ "$#" -gt 4 ]; then
149                 show_add_usage_fail "too many arguments: \"$1\""
150         fi
151
152         prepare
153
154         local entry="$1"; shift
155         local title="$1"; shift
156         local kernel="$1"; shift
157         local options="$1"; shift
158         local kernel_path="$bootdir/$kernel"
159         local entry_path="$entries_dir/${entry}.conf"
160
161         verbose "entry is \"$entry\""
162         verbose "title is \"$title\""
163         verbose "kernel is \"$kernel\""
164         verbose "options are \"$options\""
165
166         if [ -f "$entry_path" ] && [ -z "$force" ]; then
167                 fatal "gummiboot entry \"$entry_path\" already exists" \
168                       "(use -f to force re-creating it)"
169         fi
170
171         if ! [ -f "$kernel_path" ] && [ -z "$force" ]; then
172                 fatal "cannot find kernel \"$kernel_path\"" \
173                       "(use -f to ignore this error)"
174         fi
175
176         # Make sure the gummiboot configuration file exists
177         check_and_create_default_conf_file "$entry"
178
179         # Find out the kernel version from its name
180         local kernel_version="$(printf "%s" "$kernel" | LC_ALL=C \
181                    sed -e 's/[^[:digit:]]\+-\([[:digit:]]\+.*\)/\1/')"
182         [ -n "$kernel_version" ] || \
183                 fatal "cannot fetch kernel version from \"$kernel\""
184
185         # Create the new entry
186         mkdir -p $verbose -- "$entries_dir" >&2
187         cat > "$entry_path" <<-EOF
188         # Generated by $PROG
189         title $title
190         version $kernel_version
191         efi /$kernel
192         options $options
193         EOF
194
195         if [ -n "$verbose" ]; then
196                 verbose "contents of \"$entry_path\":"
197                 cat -- "$entry_path" >&2
198         fi
199 }
200
201 #
202 # -----------------------------------------------------------------------------
203 # The "remove" subcommand
204 # -----------------------------------------------------------------------------
205 #
206
207 show_remove_usage()
208 {
209         cat <<-EOF
210 Usage: $PROG remove [options] <entry>
211
212 Delete gummiboot entry <entry> (<bootdir>/loader/entries/<entry>.conf).
213
214 Options:
215   -f, --force  do not fail if the entry doesn't exist
216   -h, --help   show this text and exit
217 EOF
218 }
219
220 show_remove_usage_fail()
221 {
222         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
223         show_remove_usage >&2
224         exit 1
225 }
226
227 remove_subcommand()
228 {
229         if [ "$#" -eq 0  ]; then
230                 show_remove_usage
231                 exit 0
232         fi
233
234         local tmp
235         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
236                 show_remove_usage_fail "cannot parse command-line options"
237         eval set -- "$tmp"
238
239         local force=
240         while true; do
241                 case "$1" in
242                 -f|--force)
243                         force="-f"
244                         ;;
245                 -h|--help)
246                         show_remove_usage
247                         exit 0
248                         ;;
249                 --) shift; break
250                         ;;
251                 *) show_remove_usage_fail "unrecognized option \"$1\""
252                         ;;
253                 esac
254                 shift
255         done
256
257         if [ "$#" -lt 1 ]; then
258                 show_remove_usage_fail "too little arguments"
259         fi
260         if [ "$#" -gt 1 ]; then
261                 show_remove_usage_fail "too many arguments: \"$1\""
262         fi
263
264         prepare
265
266         local entry="$1"
267         local entry_path="$entries_dir/${entry}.conf"
268
269         if ! [ -f "$entry_path" ] && [ -z "$force" ]; then
270                 fatal "gummiboot entry \"$entry_path\" doesn't exist" \
271                       "(use -f to ignore this error)"
272         fi
273
274         rm -rf $verbose -- "$entry_path" >&2
275         verbose "removed $entry_path"
276 }
277
278 #
279 # -----------------------------------------------------------------------------
280 # The "default" subcommand
281 # -----------------------------------------------------------------------------
282 #
283
284 # Get the kernel name from a gummiboot entry
285 get_kernel_from_entry()
286 {
287         local result=
288         local entry="$entries_dir/$1.conf"
289
290         if [ -f "$entry" ]; then
291                 local regexp="$(get_regexp "efi")"
292                 local result="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
293                                 "$entry")"
294
295                 if [ -z "$result" ]; then
296                         regexp="$(get_regexp "linux")"
297                         result="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
298                                   "$entry")"
299
300                         [ -n "$result" ] || return 0
301                 fi
302         fi
303
304         printf "%s" "${result##*/}"
305 }
306
307 show_default_usage()
308 {
309         cat <<-EOF
310 Usage: $PROG default [options] <entry>
311
312 Set the default boot kernel to <entry>, which is the gummiboot entry name to
313 become the default (without the ".conf" suffix, like in
314 <bootdir>/loader/entries/<entry>.conf). If <entry> is omited, this command
315 prints the currently default entry name.
316
317 Options:
318   -f, --force  if <bootdir>/loader/loader.conf does not exist - create the
319                default one, if <bootdir>/loader/entries/<entry>.conf does not
320                exist - do not fail
321   -h, --help   show this text and exit
322 EOF
323 }
324
325 show_default_usage_fail()
326 {
327         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
328         show_default_usage >&2
329         exit 1
330 }
331
332 default_subcommand()
333 {
334         local tmp
335         tmp=`getopt -n $PROG -o f,h --long force,help -- "$@"` ||
336                 show_default_usage_fail "cannot parse command-line options"
337         eval set -- "$tmp"
338
339         local force=
340         while true; do
341                 case "$1" in
342                 -f|--force)
343                         force="-f"
344                         ;;
345                 -h|--help)
346                         show_default_usage
347                         exit 0
348                         ;;
349                 --) shift; break
350                         ;;
351                 *) show_default_usage_fail "unrecognized option \"$1\""
352                         ;;
353                 esac
354                 shift
355         done
356
357         if [ "$#" -gt 1 ]; then
358                 show_default_usage_fail "too many arguments: \"$1\""
359         fi
360
361         prepare
362
363         local entry="${1:-}";
364
365         # Make sure the gummiboot configuration file exists
366         check_and_create_default_conf_file "$entry"
367
368         # Find the current default entry
369         local regexp="$(get_regexp "default")"
370         local default_entry="$(LC_ALL=C sed -n -e "s/$regexp/\2/p" -- \
371                                 "$conf_file")"
372
373         if [ -z "$entry" ]; then
374                 printf "%s\n" "entry: $default_entry"
375                 printf "%s\n" "kernel: $(get_kernel_from_entry "$default_entry")"
376                 return 0
377         fi
378
379         local entry_path="$entries_dir/${entry}.conf"
380         if ! [ -f "$entry_path" ] && [ -z "$force" ]; then
381                 fatal "cannot find the gummiboot entry \"$entry_path\"" \
382                       "(use -f to ignore this error)"
383         fi
384
385         if [ -z "$default_entry" ]; then
386                 verbose "no default entry found, adding \"$entry\""
387                 printf "%s" "default $entry" >> "$conf_file"
388                 return 0
389         fi
390
391         [ "$(printf "%s\n" "$default_entry" | wc -l)" -eq "1" ] || \
392                 fatal "more than one default entry in \"$conf_file\""
393
394         # Escape special sed characters in "$entry" and replace the old default
395         # entry with the new one
396         local entry_esc="$(esc_sed_replacement "$entry")"
397         LC_ALL=C sed -i -e "s/$regexp/\1$entry_esc\3/" -- "$conf_file"
398
399         verbose "set the default boot kernel to \"$entry"\"
400 }
401
402 #
403 # -----------------------------------------------------------------------------
404 #
405
406 show_usage()
407 {
408         cat <<-EOF
409 Usage: $PROG [options] <subcommand> [options] <arguments>
410
411 This program changes the gummiboot bootloader configuration. Supported
412 subcommands are:
413    add     - add a gummiboot entry for a kernel
414    remove  - remove a gummiboot entry
415    default - get or set the default gummiboot entry
416
417 Run "$PROG <subcommand>" to see subcommand-specific help.
418
419 Options:
420   -b, --bootdir  path to the boot directory (default is "/boot")
421   --version      show the program version and exit
422   -v, --verbose  be verbose
423   -h, --help     show this text and exit
424 EOF
425 }
426
427 show_usage_fail()
428 {
429         IFS= printf "%s\n\n" "$PROG: error: $*" >&2
430         show_usage >&2
431         exit 1
432 }
433
434 bootdir="/boot"
435 verbose=
436 while [ -n "${1:-""}" ] && [ -z "${1##-*}" ]; do
437         case "$1" in
438         -b|--bootdir)
439                 shift
440                 # If there is no argument or it starts with "-", complain
441                 if [ -z "${1:-""}" ] || [ -z "${1##-*}" ]; then
442                         fatal "--bootdir requires an argument"
443                 fi
444                 bootdir="$1"
445                 ;;
446         --version)
447                 printf "%s\n" "$VER"
448                 exit 0
449                 ;;
450         -v|--verbose)
451                 verbose="-v"
452                 ;;
453         -h|--help)
454                 show_usage
455                 exit 0
456                 ;;
457         --) shift; break
458                 ;;
459         *) show_usage_fail "unrecognized option \"$1\""
460                 ;;
461         esac
462         shift
463 done
464
465 # Parse subcommands
466
467 if [ "$#" -eq 0 ]; then
468         show_usage
469         exit 0
470 fi
471
472
473 subcommand="$1"; shift
474
475 case "$subcommand" in
476 add)
477         add_subcommand "$@"
478         break
479         ;;
480 remove)
481         remove_subcommand "$@"
482         break
483         ;;
484 default)
485         default_subcommand "$@"
486         break
487         ;;
488 *) show_usage_fail "unrecognized subcommand \"$subcommand\""
489         ;;
490 esac