File kexec-bootloader of Package kexec-tools

#!/usr/bin/perl
#
# This program is free software; you can redistribute it and/or              {{{
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.                                                           }}}
#
use Bootloader::Tools;
use strict;
use Getopt::Long;

use constant FALSE => 0;
use constant TRUE  => 1;

my $GRUBDIR = "/boot/grub";
my $GRUBDEFAULT = "$GRUBDIR/default";
my $debug = FALSE;
my $showHelp = FALSE;

#
# Prints the given stuff (variable number of arguments) if debugging has
# been enabled. Does nothing otherwise.
sub print_debug(@)                                                         # {{{
{
    if ($debug) {
        print STDERR @_;
        print STDERR "\n";
    }
}                                                                          # }}}

#
# Displays help. Does not exit.
sub show_help()
{
    print STDERR "kexec-bootloader\n";
    print STDERR "Loads kexec kernel from bootloader configuration.\n\n";
    print STDERR "Options:\n";
    print STDERR "  -h | --help    Shows that help message.\n";
    print STDERR "  -D | --debug   Prints debugging information.\n";
}


#
# Returns the value stored with "grubonce". If no value has been stored
# or the /boot/grub/default file is not readable, then -1 is returned.
#
# Also emulate the behaviour when using GRUB which resets the 'magic once' flag
# when booting. Because we use kexec, we have to reset that 'magic once' flag
# ourselves.
sub get_grubonce_and_reset_magic()                                         # {{{
{
    # no /boot/grub/default file
    if (! -f $GRUBDEFAULT) {
        print_debug("get_grubonce_and_reset_magic(): No $GRUBDEFAULT.");
        return -1;
    }

    # read /boot/grub/default
    open(FH, $GRUBDEFAULT) or return -1;
    my $value;
    my $ret = sysread(FH, $value, 10);
    close(FH);

    # only if we have read 4 bytes it's valid
    if ($ret != 10) {
        print_debug("get_grubonce_and_reset_magic(): ".
            "Read returned $ret instead of 4.");
        return -1;
    }


    $value =~ s/\n//g;
    my $once = int($value);

    # 0x4000 is the "magic once flag"
    unless ($once & 0x4000) {
        print_debug("get_grubonce_and_reset_magic(): No magic 0x40000.");
        return -1;
    }

    my $defaultno = $once & ~0x4000;
    my $buf = $defaultno . "\0" .  "\n" x 9;

    # now reset the grubonce flag
    open(FH, ">$GRUBDEFAULT") or return $defaultno;
    $ret = syswrite(FH, $buf, 10);
    close(FH);

    return $defaultno;
}                                                                          # }}}

#
# Parses command line options and sets global variables.
sub parse_options()                                                        # {{{
{
    GetOptions(
        "D|debug"  => \$debug,
        "h|help"   => \$showHelp
    );
}                                                                          # }}}


parse_options();
if ($showHelp) {
    show_help();
    exit(0);
}
Bootloader::Tools::InitLibrary();
my $loader = Bootloader::Tools::GetBootloader();
my $default = -1;

if ($loader =~ m/GRUB/i) {
    $default = get_grubonce_and_reset_magic();
    print_debug("GRUB Default: $default");
}

my $section = undef;
# do we have a default?
if ($default >= 0) {
    my @sections = Bootloader::Tools::GetSectionList();
    print_debug("Number of sections: " . $#sections);

    if ($#sections < 0 || $#sections < $default) {
        print STDERR "WARNING: grubonce default number ($default) is invalid.\n";
        print STDERR "         Falling back to the default GRUB section.\n";
    } else {
        my $sect_name = $sections[$default];
        $section = Bootloader::Tools::GetSection($sect_name);
    }
}

# use the default section if we didn't get any default otherwise
if (!$section) {
    $section = Bootloader::Tools::GetDefaultSection();
}

if (!$section) {
    print STDERR "Unable to get default section of bootloader configuration.\n";
    exit(1);
}

if ($debug) {
    print "Type   : " . $section->{"type"}."\n";
    print "Name   : " . $section->{"name"}."\n";
    print "Image  : " . $section->{"image"}."\n";
    print "Initrd : " . $section->{"initrd"}."\n";
    print "VGA    : " . $section->{"vgamode"}."\n";
    print "Append : " . $section->{"append"}."\n";
    print "Root   : " . $section->{"root"}."\n";
}

if ($section->{"type"} ne "image") {
    print STDERR "Default boot section is no image.\n";
    exit(1);
}

# check if the image exists
if (! -f $section->{"image"}) {
    print STDERR "Image '" . $section->{"image"} . "' does not exist.\n";
    exit(1);
}

# check if the initrd exists
if ($section->{"initrd"} && ! -f $section->{"initrd"}) {
    print STDERR "Initrd '" . $section->{"initrd"} . "' does not exist.\n";
    exit(1);
}

# Do we have kexec?
if (system("which kexec &>/dev/null") != 0) {
    print STDERR "kexec not available. Install kexec-tools.\n";
    exit(1);
}

# build the command list
my $cmd = "kexec";

# append image
$cmd .= " -l '" . $section->{"image"} . "'";

# append initrd if available
if ($section->{"initrd"}) {
    $cmd .= " --initrd='" . $section->{"initrd"} . "'";
}

# build append line
my $append = "";
if ($section->{"root"}) {
    $append .= "root=" . $section->{"root"};
}
if ($section->{"vga"}) {
    $append .= " vga=" . $section->{"vga"};
}
if ($section->{"append"}) {
    $append .= " " . $section->{"append"};
}

# and tell that kexec
$cmd .= " --append='" . $append . "'";

print_debug("Kexec call: " . $cmd);
if (system($cmd) != 0) {
    print STDERR "kexec($cmd) failed.";
    exit(1);
}

exit(0);

# :vim set ts=4 sw=4 et fdm=markers: :collapseFolds=1: