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