#! /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 , --initrd , --xen-kernel , --xen, --default, --previous, --name , --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 specify path to kernel image =item B<--initrd> F 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 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 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 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, "; # 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 ($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 if (($loader eq "grub" || $loader eq "lilo") && defined(test_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_name =~ s/^.*(xen.*)$/$1/; $opt_name = ucfirst ($opt_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; } # Create short labels for all other loaders else { $opt_name = $flavor; } } } # # execute selected operation # if (defined $oper{add}) { open (LOG, ">>$logname"); print LOG ("update-bootloader: now executing operation add\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 { my $default_image = GetDefaultImage(); # Check if default image is a symlink. If this is the case, let the new # kernel be the default one. if (-l $default_image) { $default_image = readlink ($default_image); push @params, default => $opt_default; } # Else, if option $opt_force_default is set, let this new kernel be # the default one. elsif ($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 { $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; pod2usage("There is no section with image '$opt_image'"); } open (LOG, ">>$logname"); print LOG ("update-bootloader: finished operation remove\n"); close LOG; } if (defined $oper{refresh}) { UpdateBootloader(); } # # Local variables: # mode: perl # mode: font-lock # mode: auto-fill # fill-column: 78 # End: #