#!/bin/bash set -e -o pipefail . /etc/functions TRACE_FUNC bootdir="$1" file="$2" if [ -z "$bootdir" -o -z "$file" ]; then die "Usage: $0 /boot /boot/grub/grub.cfg" fi reset_entry() { name="" kexectype="elf" kernel="" initrd="" modules="" append="" } filedir=`dirname $file` DEBUG "filedir= $filedir" bootdir="${bootdir%%/}" DEBUG "bootdir= $bootdir" bootlen="${#bootdir}" DEBUG "bootlen= $bootlen" appenddir="${filedir:$bootlen}" DEBUG "appenddir= $appenddir" fix_path() { path="$@" if [ "${path:0:1}" != "/" ]; then DEBUG "fix_path: path was $@" path="$appenddir/$path" DEBUG "fix_path: path is now $path" fi } # GRUB kernel lines (linux/multiboot) can include a command line. Check whether # the file path exists in $bootdir. check_path() { local checkpath firstval checkpath="$1" firstval="$(echo "$checkpath" | cut -d\ -f1)" if ! [ -r "$bootdir$firstval" ]; then DEBUG "$bootdir$firstval doesn't exist" return 1; fi return 0 } echo_entry() { if [ -z "$kernel" ]; then return; fi fix_path $kernel # The kernel must exist - if it doesn't, ignore this entry, it # wouldn't work anyway. This could happen if there was a # GRUB variable in the kernel path, etc. if ! check_path "$path"; then return; fi entry="$name|$kexectype|kernel $path" case "$kexectype" in elf) if [ -n "$initrd" ]; then for init in $(echo $initrd | tr ',' ' '); do fix_path $init # The initrd must also exist if ! check_path "$path"; then return; fi entry="$entry|initrd $path" done fi if [ -n "$append" ]; then entry="$entry|append $append" fi ;; multiboot|xen) entry="$entry$modules" ;; *) return ;; esac # Double-expand here in case there are variables in the kernel # parameters - some configs do this and can boot with empty # expansions (Debian Live ISOs use this for loopback boots) echo $(eval "echo \"$entry\"") } search_entry() { case $line in menuentry* | MENUENTRY* ) state="grub" reset_entry name=`echo $line | tr "'" "\"" | cut -d\" -f 2` ;; label* | LABEL* ) state="syslinux" reset_entry name=`echo $line | cut -c6- ` esac } grub_entry() { if [ "$line" = "}" ]; then echo_entry state="search" return fi # add info to menuentry trimcmd=`echo $line | tr '\t ' ' ' | tr -s ' '` cmd=`echo $trimcmd | cut -d\ -f1` val=`echo $trimcmd | cut -d\ -f2-` case $cmd in multiboot*) # TODO: differentiate between Xen and other multiboot kernels kexectype="xen" kernel="$val" DEBUG " grub_entry multiboot kernel= $kernel" ;; module*) case $val in --nounzip*) val=`echo $val | cut -d\ -f2-` ;; esac fix_path $val modules="$modules|module $path" DEBUG " grub_entry linux modules= $modules" ;; linux*) # Some configs have a device specification in the kernel # or initrd path. Assume this would be /boot and remove # it. Keep the '/' following the device, since this # path is relative to the device root, not the config # location. DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" kernel=`echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2` append=`echo $trimcmd | cut -d\ -f3-` ;; initrd*) # Trim off device specification as above initrd="$(echo "$val" | sed "s/([^)]*)//g")" DEBUG " grub_entry: linux initrd= $initrd" ;; esac } syslinux_end() { # finish menuentry # attempt to parse out of append if missing initrd if [ -z "$initrd" ]; then newappend="" for param in $append; do case $param in initrd=*) initrd=`echo $param | cut -d\= -f2` ;; *) newappend="$newappend $param" ;; esac done append="${newappend##' '}" fi appenddir="$(echo $appenddir | cut -d\/ -f -2)" echo_entry state="search" } syslinux_multiboot_append() { splitval=`echo "${val// --- /|}" | tr '|' '\n'` while read line do if [ -z "$kernel" ]; then kernel="$line" else fix_path $line modules="$modules|module $path" fi done << EOF $splitval EOF } syslinux_entry() { case $line in "") syslinux_end return ;; label* | LABEL* ) syslinux_end search_entry return ;; esac # add info to menuentry trimcmd=`echo $line | tr '\t ' ' ' | tr -s ' '` cmd=`echo $trimcmd | cut -d\ -f1` val=`echo $trimcmd | cut -d\ -f2-` case $trimcmd in menu* | MENU* ) cmd2=`echo $trimcmd | cut -d \ -f2` if [ "$cmd2" = "label" -o "$cmd2" = "LABEL" ]; then name=`echo $trimcmd | cut -c11- | tr -d '^'` fi ;; linux* | LINUX* | kernel* | KERNEL* ) case $val in # TODO: differentiate between Xen and other multiboot kernels *mboot.c32) kexectype="xen" ;; *.c32) # skip this entry state="search" ;; *) kernel="$val" DEBUG "kernel= $kernel" esac ;; initrd* | INITRD* ) initrd="$val" DEBUG "initrd= $initrd" ;; append* | APPEND* ) if [ "$kexectype" = "multiboot" -o "$kexectype" = "xen" ]; then syslinux_multiboot_append else append="$val" DEBUG "append= $append" fi ;; esac } state="search" while read line do case $state in search) search_entry ;; grub) grub_entry ;; syslinux) syslinux_entry ;; esac done < "$file" # handle EOF case if [ "$state" = "syslinux" ]; then syslinux_end fi