perl-Bootloader/update-bootloader

523 lines
13 KiB
Perl

#! /usr/bin/perl
use POSIX;
use Getopt::Long;
use Pod::Usage;
use Bootloader::Tools;
use Bootloader::Path;
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 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
=head1 DESCRIPTION
B<update-bootloader> will let you modify your bootloader configuration using
Bootloader::Tools perl module.
=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' ,
'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 (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'");
}
} 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 =~ /-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:
#