523 lines
13 KiB
Perl
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:
|
|
#
|