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