File update-bootloader of Package perl-Bootloader

#! /usr/bin/perl

use POSIX;
use Getopt::Long;
use Pod::Usage;
use Bootloader::Tools;
use Bootloader::Path;
use Bootloader::MBRTools;
use strict;

my %oper;
my ($opt_default, $opt_force, $opt_force_default, $opt_help, $opt_man, $opt_previous, $opt_xen)
    = (0,0,0,0,0,0,0);
my ($opt_image, $opt_initrd, $opt_name, $opt_xen_name, $opt_failsafe, $opt_xen_kernel) 
    = ('','','','','',undef);
my $add_product = 0;

=head1 NAME

update-bootloader - update/change bootloader configuration using
    Bootloader::Tools perl module

=head1 SYNOPSIS

update-bootloader [operation] [options]

operation is one of --add, --remove or --refresh.

valid options are --help, --man, --image <file>, --initrd <file>,
--xen-kernel <file>, --xen, --default, --previous, --name <string>, --force,
--force-default.

=head1 DESCRIPTION

B<update-bootloader> will let you modify your bootloader configuration using
Bootloader::Tools perl module.

=head1 OPERATIONS

=over 8

=item B<--add>

add an new image section.
Needs a call to --refresh to take effect.

=item B<--remove>

remove the specified image section.
Needs a call to --refresh to take effect.

=item B<--refresh>

activate the new config e.g. write boot loader to disk

=back

=head1 PARAMETER

=over 8

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.
   
=item B<--image> F<file>

specify path to kernel image

=item B<--initrd> F<file>

specify path to initrd file

=item B<--xen>

specify that you what to add a xen and not a regular image section

=item B<--xen-kernel> F<file>

specify that you what to add a xen section with a specific image.
Implies --xen option.

=item B<--default>

let the new section to be added be the default section if appropriate. Only
allowed together with --add operation 

=item B<--previous>

set some usuable defaults for image, initrd and name when

=item B<--name> F<string>

specify the name of the section to be added/removed

=item B<--force>

dont complain, just do the right thing

=item B<--force-default>

force the new section to be added to be the default section. Only
allowed together with --add operation

=back

=cut

# Get product name and version
# FIXME: There is still no guaranteed-to-work solution
# FIXME: as Commandline:: always prints to stderr, which is risky
# FIXME: to get garbage.
# If Commandline:: prints garbage, use /etc/SuSE-release instead  
 
sub GetProduct {
    my $namever;
     
    # first try: new zypp-query-pool adopted to our needs
    my @product_query = split ('\|', qx{/usr/lib/zypp/zypp-query-pool products \@system});
    my $found = 0;
    my $i = 0;

    for ($i = 0; $i <=  @product_query; $i++){
	if ($product_query[$i] eq "base"){
            $found = 1;
	    last;
	}
    }

    # Determine the bootloader type, because long product names may only 
    # be used in case of grub
    my $loader = Bootloader::Tools::GetBootloader();
    # Substitude whitespaces with an underscore
    if ($found){
        if ($loader ne "grub") {
            $namever = $product_query[$i+1];
	    $namever =~ s/\s/_/g;
        }
        else{
            $namever = $product_query[$i+2];
        }
        chomp $namever;
    }
    return "$namever" if $namever ne '' and $namever !~ /\n/;

    # Second try: Is there a usable /etc/SuSE-release?
    # This should really not be used anymore, as the syntax changed
    # no 'SP1' in the output. 
    if (open(RELEASE, "</etc/SuSE-release") && $loader eq "grub") {
	# first line is sufficient
	$namever = <RELEASE>;

	# delete everything starting with the first parenthesis
	$namever =~ s/\s*\(.*//; 

	close(RELEASE);
	chomp $namever;
	return "$namever";
    }

    # the last line of defense ...
    return "Linux";
}

sub test_gettext {
  my $filename = "Locale/gettext.pm";
  my $realfilename;
  foreach my $prefix (@INC) {
    $realfilename = "$prefix/$filename";
    if (-f $realfilename) {
      return 1;
    }
  }
  return undef
}

my $logname = Bootloader::Path::Logname();
open (LOG, ">>$logname");
print LOG ("update-bootloader called:\n" . $0 . " " . join (" ", @ARGV) .  "\n");
close LOG;

GetOptions (\%oper,
    'add|a'    	 ,
    'refresh'    ,
    'remove|r' 	 ,
    'examinembr|e=s', 
    'default|d'  => \$opt_default,
    'force-default' => \$opt_force_default,
    'help|h'   	 => \$opt_help,
    'force'      => \$opt_force,
    'image=s'  	 => \$opt_image,
    'initrd=s' 	 => \$opt_initrd,
    'man|m'    	 => \$opt_man,
    'xen'        => \$opt_xen,
    'xen-kernel=s' => \$opt_xen_kernel,
    'name=s'   	 => \$opt_name,
    'previous|p' => \$opt_previous)
    or pod2usage(2);
pod2usage(1) if $opt_help;
pod2usage(-exitstatus => 0, -verbose => 2) if $opt_man;

pod2usage("Specify exactly one operation, either 'add', 'remove' or 'refresh'")
    unless scalar keys(%oper) == 1;

pod2usage("Option 'default' is only allowed for operation 'add'")
    if ($opt_default and not defined $oper{add});

pod2usage("Option 'force-default' is only allowed for operation 'add'")
    if ($opt_force_default and not defined $oper{add});
    
if (defined $oper{"examinembr"}) {
  my $ret = Bootloader::MBRTools::examineMBR($oper{"examinembr"});
  if (defined $ret){
    print "$ret\n";
    exit 0;
  } else {
    exit 1;
  }
}

if (Bootloader::Tools::GetBootloader() eq "none")
{
  open (LOG, ">>$logname");
  print LOG ("none bootloader, skipped updating \n");
  close LOG;
  
  exit 0;
}

if ($opt_image and $opt_image !~ m;^/;) {
    $opt_image = getcwd . '/' . $opt_image
}
if ($opt_initrd and $opt_initrd !~ m;^/;) {
    $opt_initrd = getcwd . '/' . $opt_initrd;
}

if (defined $opt_xen_kernel) {
    $opt_xen = 1;
} elsif ($opt_xen) {
    my $xen_flavor = $opt_image;
    $xen_flavor =~ s/.*-(\w+)/$1/;

    if ($xen_flavor eq "xenpae") {
        $opt_xen_kernel = "/boot/xen-pae.gz";
    }
    else {
        $opt_xen_kernel = "/boot/xen.gz";
    }
}
my $type = $opt_xen ? "xen" : "image";

InitLibrary();


if ($opt_previous) {
    unless ($opt_image)	{
    	$opt_image = GetDefaultImage() . ".previous";
    }
    unless($opt_initrd) {
    	$opt_initrd = GetDefaultInitrd() . ".previous";
    }	
}

# FIXME: these section names should be unified somehow together with multi
# language and grafical menu handling
if (defined $oper{add}) {
    my $loader = Bootloader::Tools::GetBootloader();

    unless ($opt_name) {
	if ($opt_xen and $opt_previous) {
	    if ($loader eq "grub" || $loader eq "lilo") {
		$opt_name = "Previous Xen";
		$add_product = 1;
	    }
	    else {
		$opt_name = "previous xen";
	    }
	}
	elsif ($opt_xen) {
	    if ($loader eq "grub" || $loader eq "lilo") {
		$opt_name = "Xen";
		$add_product = 1;
	    }
	    else {
		$opt_name = "xen";
	    }
	}
	elsif ($opt_previous) {
	    if ($loader eq "grub" || $loader eq "lilo") {
		$opt_name = "Previous Kernel";
		$add_product = 1;
	    }
	    else {
		$opt_name = "previous";
	    }
	}
	else {
	    $opt_name = $opt_image;
	    $opt_name =~ s/.*\///;
	}
    }

    # only localize on grub and lilo
    #FIXME jreidinger: I think this now doesn't work. because pbl get only version + type of kernel....maybe help translate strings like failsafe etc
    if (($loader eq "grub" || $loader eq "lilo") && defined(test_gettext())) {
        require Locale::gettext;
	setlocale(LC_MESSAGES, Bootloader::Tools::GetSystemLanguage());

	my $d =  Locale::gettext->domain("bootloader");
	$d->dir("/usr/share/YaST2/locale");
	my $opt_trans = $d->get($opt_name);
	chomp ($opt_trans);

        #log what is translated
        my $logname = Bootloader::Path::Logname();
        open (LOG, ">>$logname");
        print LOG ("translate :\n" . $0 . " " . join (" ", @ARGV) .  "\n");
        close LOG;
	
	# check whether translation only contains [a-zA-Z0-9 _]
	# otherwise fall back to untranslated string
	if ($opt_trans =~ /^[a-zA-Z\d _]+$/g ) {
	    $opt_name = $opt_trans;
	}
    }

    # Naming scheme for default, smp, bigsmp and pae kernels
    if (($opt_name =~ /-default/) ||
	($opt_name =~ /-smp/) ||
	($opt_name =~ /-bigsmp/) ||
	($opt_name =~ /-pae/)) {

	if ($loader eq "grub") {
	    $opt_name =~ s/-[^-]*$//;
	    $opt_failsafe = "Failsafe -- " . GetProduct() . " - " . $opt_name;
	    $opt_name = GetProduct() . " - " . $opt_name;
	}
	else {
	    $opt_failsafe = "Failsafe";
	    $opt_name = GetProduct();
	}
    }
    # Naming scheme for all xen kernels, thus xen and xenpae
    elsif ($opt_xen) {
	if ($loader eq "grub") {
	    my $version = $opt_name;
	    $version =~ s/-xen.*$//;

	    $opt_name =~ s/^.*(xen.*)$/$1/;
	    $opt_name = ucfirst ($opt_name);

	    $opt_xen_name = "$opt_name -- " . GetProduct() . " - " . $version;
	}
	else {
	    $opt_xen_name =~ s/^.*(xen.*)$/$1/;
	    $opt_xen_name = ucfirst ($opt_xen_name);
	}
    }
    # Naming scheme for all other kernels
    else {
	my $flavor = $opt_name;
	$flavor =~ s/.*-(\w+)/$1/;
	$flavor = ucfirst ($flavor);

	# Create long labels for grub
	if ($loader eq "grub") {
	    my $version = $opt_name;
	    $version =~ s/-[^-]*$//;

	    $opt_name = $flavor . " -- " . GetProduct() . " - " . $version;
	    $opt_failsafe = "Failsafe -- " . GetProduct() . " - " . $version;
	}
	# Create short labels for all other loaders
	else {
	    $opt_name = GetProduct();
	    $opt_failsafe = "Failsafe";
	}
    }
}

#
#  execute selected operation
#
if (defined $oper{add}) {
    open (LOG, ">>$logname");
    print LOG ("update-bootloader: now executing operation add\n");
    print LOG ("update-bootloader: changed opt name is $opt_name \n");
    close LOG;

    pod2usage("Please specify name and kernel image for new section")
	unless $opt_name and $opt_image;

    my @params = (
		  type  => $type,
		  image => $opt_image,
    );
    if (CountSections(@params) != 0) {
    	if (not $opt_force) {
	    pod2usage("There are already sections with image '$opt_image'");
	}
        open (LOG, ">>$logname");
        print LOG ("update-bootloader: section already exist. Skip add.\n");
        close LOG;
    } else {

	# If, if option $opt_force_default is set, let this new kernel be
	# the default one.
	if ($opt_force_default) {
	    push @params, default => $opt_default;
	}

	# Else, find out the flavor of the default kernel. If it is the same
	# flavor as the one of the new kernel, let the new kernel be the
	# default one.
	else {
	    my $default_image = GetDefaultImage();

	    if (-l $default_image) {
	      $default_image = readlink ($default_image);
            }

	    $default_image =~ s/^.*-//;

	    if ($opt_image =~ m/.*-${default_image}$/) {
		push @params, default => $opt_default;
	    }
	}

	push @params, xen     => $opt_xen_kernel if $type eq "xen";
	push @params, initrd  => $opt_initrd if $opt_initrd;

	# Add "xen" section
	if ($opt_xen) { 
	    # Add original_name to params to be able to create comment line
	    push @params, original_name => "xen";

            open (LOG, ">>$logname");
            print LOG ("update-bootloader: calling Tools::AddSection (XEN)\n");
            close LOG;

	    AddSection($opt_xen_name, @params);
	}
	# Add "normal" section
	else {
	    # Add original_name to params to be able to create comment line
	    push @params, original_name => "linux";

            open (LOG, ">>$logname");
            print LOG ("update-bootloader: calling Tools::AddSection (normal)\n");
            close LOG;

	    AddSection($opt_name, @params);
	}

 	my $arch = `uname --hardware-platform`;
	chomp ($arch);

	# Add a "Failsafe" section, but only if the underlying architecture is
	# one of i386, x86_84 or ia64 and the kernel flavor is either default,
	# smp, bigsmp or pae and not a xen kernel.
	if ((($arch eq "i386") ||
	    ($arch eq "x86_64") ||
	    ($arch eq "ia64")) &&
	    (($opt_image =~ /-default/) ||
	    ($opt_image =~ /-smp/) ||
	    ($opt_image =~ /-desktop/) ||
	    ($opt_image =~ /-bigsmp/) ||
	    ($opt_image =~ /-pae/)) &&
	    ($opt_xen != 1)) {
	    # Add original_name to params to be able to create comment line
	    push @params, original_name => "failsafe";

            open (LOG, ">>$logname");
            print LOG ("update-bootloader: calling Tools::AddSection (failsafe)\n");
            close LOG;

	    AddSection($opt_failsafe, @params);
	}
    }
}

if (defined $oper{remove}) {
    my @params = (
		  type  => $type,
		  image => $opt_image,
    );
    push @params, xen    => $opt_xen_kernel if $type eq "xen";
    push @params, initrd => $opt_initrd if $opt_initrd;
    push @params, name   => $opt_name if $opt_name;
		      
    open (LOG, ">>$logname");
    print LOG ("update-bootloader: now executing operation remove\n");
    close LOG;

    my $num = CountSections(@params);

    if ($num > 0) {
        if ($num > 1 and not $opt_force) {
            open (LOG, ">>$logname");
            print LOG ("update-bootloader: found $num sections, no opt_force: not removing\n");
            close LOG;

	    pod2usage("There is more than one section with image '$opt_image'");
	} else {
            open (LOG, ">>$logname");
            print LOG ("update-bootloader: calling Tools::RemoveSections\n");
            close LOG;

	    RemoveSections(@params);
	}
    } else {
	open (LOG, ">>$logname");
	print LOG ("update-bootloader: no $opt_image found\n");
	close LOG;
    }
    open (LOG, ">>$logname");
    print LOG ("update-bootloader: finished operation remove\n");
    close LOG;

}

if (defined $oper{refresh}) {
    my $ret = UpdateBootloader();
    exit 1 if ( !$ret );
}

#
# Local variables:
#     mode: perl
#     mode: font-lock
#     mode: auto-fill
#     fill-column: 78
# End:
#
openSUSE Build Service is sponsored by