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