| #!/usr/bin/perl |
| =head1 NAME |
| |
| Linux::Bootloader - Base class interacting with Linux bootloaders |
| |
| =head1 SYNOPSIS |
| |
| |
| my $bootloader = new Linux::Bootloader(); |
| my $config_file='/boot/grub/menu.lst'; |
| |
| $bootloader->read($config_file); |
| $bootloader->print_info('all'); |
| $bootloader->add(%hash); |
| $bootloader->update(%hash); |
| $bootloader->remove(2); |
| $bootloader->get_default(); |
| $bootloader->set_default(2); |
| %hash = $bootloader->read_entry(0); |
| $bootloader->write($config_file); |
| |
| |
| =head1 DESCRIPTION |
| |
| This module provides base functions for working with bootloader configuration files. |
| |
| =head1 FUNCTIONS |
| |
| =head2 new() |
| |
| Creates a new Linux::Bootloader object. |
| |
| =head2 read() |
| |
| Reads configuration file into an array. |
| Takes: string. |
| Returns: undef on error. |
| |
| =head2 write() |
| |
| Writes configuration file. |
| Takes: string. |
| Returns: undef on error. |
| |
| =head2 print_info() |
| |
| Prints information from config. |
| Takes: string. |
| Returns: undef on error. |
| |
| =head2 _info() |
| |
| Parse config into array of hashes. |
| Takes: nothing. |
| Returns: array of hashes. |
| |
| =head2 get_default() |
| |
| Determine current default kernel. |
| Takes: nothing. |
| Returns: integer, undef on error. |
| |
| =head2 set_default() |
| |
| Set new default kernel. |
| Takes: integer. |
| Returns: undef on error. |
| |
| =head2 add() |
| |
| Add new kernel to config. |
| Takes: hash. |
| Returns: undef on error. |
| |
| =head2 update() |
| |
| Update args of an existing kernel entry. |
| Takes: hash. |
| Returns: undef on error. |
| |
| =head2 remove() |
| |
| Remove kernel from config. |
| Takes: string. |
| Returns: undef on error. |
| |
| =head2 read_entry() |
| |
| Read an existing entry into a hash suitable to add or update from. |
| Takes: integer or title |
| Returns: undef or hash |
| |
| =head2 debug($level) |
| |
| Sets or gets the current debug level, 0-5. |
| Returns: Debug level |
| |
| =head2 _check_config() |
| |
| Conducts a basic check for kernel validity |
| Returns: true if checks out okay, |
| false if not okay, |
| undef on error |
| |
| =head2 _lookup() |
| |
| Converts title into position. |
| Takes: string. |
| Returns: integer, |
| undef on error |
| |
| =cut |
| |
| |
| package Linux::Bootloader; |
| |
| use strict; |
| use warnings; |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub new { |
| my $this = shift; |
| my $class = ref($this) || $this; |
| if ( defined $class and $class eq 'Linux::Bootloader' ){ |
| my $detected_bootloader = Linux::Bootloader::Detect::detect_bootloader(); |
| unless (defined $detected_bootloader) { return undef; } |
| $class = "Linux::Bootloader::" . "\u$detected_bootloader"; |
| eval" require $class; "; |
| } |
| my $self = bless ({}, $class); |
| $self->{config_file} = shift; |
| unless (defined $self->{'config_file'}){ |
| $self->_set_config_file(); |
| } |
| |
| $self->{config} = []; |
| $self->{debug} = 0; |
| $self->{'entry'} = {}; |
| |
| return $self; |
| } |
| |
| |
| ### Generic Functions ### |
| |
| # Read config file into array |
| |
| sub read { |
| my $self=shift; |
| my $config_file=shift || $self->{config_file}; |
| print ("Reading $config_file.\n") if $self->debug()>1; |
| |
| open(CONFIG, "$config_file") |
| || warn ("ERROR: Can't open $config_file.\n") && return undef; |
| @{$self->{config}}=<CONFIG>; |
| close(CONFIG); |
| |
| print ("Current config:\n @{$self->{config}}") if $self->debug()>4; |
| print ("Closed $config_file.\n") if $self->debug()>2; |
| return 1; |
| } |
| |
| |
| # Write new config |
| |
| sub write { |
| my $self=shift; |
| my $config_file=shift || $self->{config_file}; |
| my @config=@{$self->{config}}; |
| |
| return undef unless $self->_check_config(); |
| |
| print ("Writing $config_file.\n") if $self->debug()>1; |
| print join("",@config) if $self->debug() > 4; |
| |
| if (-w $config_file) { |
| system("cp","$config_file","$config_file.bak.boottool"); |
| if ($? != 0) { |
| warn "ERROR: Cannot backup $config_file.\n"; |
| return undef; |
| } else { |
| print "Backed up config to $config_file.bak.boottool.\n"; |
| } |
| |
| open(CONFIG, ">$config_file") |
| || warn ("ERROR: Can't open config file.\n") && return undef; |
| print CONFIG join("",@config); |
| close(CONFIG); |
| return 0; |
| } else { |
| print join("",@config) if $self->debug() > 2; |
| warn "WARNING: You do not have write access to $config_file.\n"; |
| return 1; |
| } |
| } |
| |
| |
| # Parse config into array of hashes |
| |
| sub _info { |
| my $self=shift; |
| |
| return undef unless $self->_check_config(); |
| my @config=@{$self->{config}}; |
| |
| # remove garbarge - comments, blank lines |
| @config=grep(!/^#|^\n/, @config); |
| |
| my %matches = ( default => '^\s*default[\s+\=]+(\S+)', |
| timeout => '^\s*timeout[\s+\=]+(\S+)', |
| title => '^\s*label[\s+\=]+(\S+)', |
| root => '^\s*root[\s+\=]+(\S+)', |
| args => '^\s*append[\s+\=]+(.*)', |
| initrd => '^\s*initrd[\s+\=]+(\S+)', |
| ); |
| |
| my @sections; |
| my $index=0; |
| foreach (@config) { |
| if ($_ =~ /^\s*(image|other)[\s+\=]+(\S+)/i) { |
| $index++; |
| $sections[$index]{'kernel'} = $2; |
| } |
| foreach my $key (keys %matches) { |
| if ($_ =~ /$matches{$key}/i) { |
| $sections[$index]{$key} = $1; |
| $sections[$index]{$key} =~ s/\"|\'//g if ($key eq 'args'); |
| } |
| } |
| } |
| |
| # sometimes config doesn't have a default, so goes to first |
| if (!(defined $sections[0]{'default'})) { |
| $sections[0]{'default'} = '0'; |
| |
| # if default is label name, we need position |
| } else { |
| foreach my $index (1..$#sections) { |
| if ($sections[$index]{'title'} eq $sections[0]{'default'}) { |
| $sections[0]{'default'} = $index-1; |
| last; |
| } |
| } |
| } |
| |
| # if still no valid default, set to first |
| if ( $sections[0]{'default'} !~ m/^\d+$/ ) { |
| $sections[0]{'default'} = 0; |
| } |
| |
| # return array of hashes |
| return @sections; |
| } |
| |
| |
| # Determine current default kernel |
| |
| sub get_default { |
| my $self = shift; |
| |
| print ("Getting default.\n") if $self->debug()>1; |
| return undef unless $self->_check_config(); |
| |
| my @sections = $self->_info(); |
| my $default = $sections[0]{'default'}; |
| if ($default =~ /^\d+$/) { |
| return 0+$default; |
| } |
| |
| } |
| |
| # Find the template entry. |
| sub get_template { |
| my ($self) = @_; |
| |
| print ("Getting template.\n") if $self->debug()>1; |
| return undef unless $self->_check_config(); |
| |
| my @sections = $self->_info(); |
| my $default = $sections[0]{'default'} + 1; |
| |
| if (defined $sections[$default]{'kernel'}) { |
| return $default - 1; |
| } |
| for ($default = 1; $default <= $#sections; $default++) { |
| if (defined $sections[$default]->{'kernel'}) { |
| return $default - 1; |
| } |
| } |
| return undef; |
| } |
| |
| |
| # Set new default kernel |
| |
| sub set_default { |
| my $self=shift; |
| my $newdefault=shift; |
| |
| print ("Setting default.\n") if $self->debug()>1; |
| |
| return undef unless defined $newdefault; |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if ($newdefault !~ /^\d+$/) { |
| $newdefault = $self->_lookup($newdefault); |
| } |
| |
| my $kcount = $#sections-1; |
| if ((!defined $newdefault) || ($newdefault < 0) || ($newdefault > $kcount)) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| |
| # convert position to title |
| $newdefault = $sections[++$newdefault]{title}; |
| |
| foreach my $index (0..$#config) { |
| if ($config[$index] =~ /^\s*default/i) { |
| $config[$index] = "default=$newdefault # set by $0\n"; |
| last; |
| } |
| } |
| @{$self->{config}} = @config; |
| } |
| |
| |
| # Add new kernel to config |
| |
| sub add { |
| my $self=shift; |
| my %param=@_; |
| |
| print ("Adding kernel.\n") if $self->debug()>1; |
| |
| if (!defined $param{'add-kernel'} && defined $param{'kernel'}) { |
| $param{'add-kernel'} = $param{'kernel'}; |
| } elsif (!defined $param{'add-kernel'} || !defined $param{'title'}) { |
| warn "ERROR: kernel path (--add-kernel), title (--title) required.\n"; |
| return undef; |
| } elsif (!(-f "$param{'add-kernel'}")) { |
| warn "ERROR: kernel $param{'add-kernel'} not found!\n"; |
| return undef; |
| } elsif (defined $param{'initrd'} && !(-f "$param{'initrd'}")) { |
| warn "ERROR: initrd $param{'initrd'} not found!\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| # remove title spaces and truncate if more than 15 chars |
| $param{title} =~ s/\s+//g; |
| $param{title} = substr($param{title}, 0, 15) if length($param{title}) > 15; |
| |
| my @sections=$self->_info(); |
| |
| # check if title already exists |
| if (defined $self->_lookup($param{title})) { |
| warn ("WARNING: Title already exists.\n"); |
| if (defined $param{force}) { |
| $self->remove($param{title}); |
| } else { |
| return undef; |
| } |
| } |
| |
| my @config = @{$self->{config}}; |
| @sections=$self->_info(); |
| |
| # Use default kernel to fill in missing info |
| my $default=$self->get_template(); |
| $default++; |
| |
| foreach my $p ('args', 'root') { |
| if (! defined $param{$p}) { |
| $param{$p} = $sections[$default]{$p}; |
| } |
| } |
| |
| # use default entry to determine if path (/boot) should be removed |
| my $bootpath = $sections[$default]{'kernel'}; |
| $bootpath =~ s@[^/]*$@@; |
| |
| $param{'add-kernel'} =~ s@^/boot/@$bootpath@; |
| $param{'initrd'} =~ s@^/boot/@$bootpath@ unless !defined $param{'initrd'}; |
| |
| my @newkernel; |
| push (@newkernel, "image=$param{'add-kernel'}\n", "\tlabel=$param{title}\n"); |
| push (@newkernel, "\tappend=\"$param{args}\"\n") if defined $param{args}; |
| push (@newkernel, "\tinitrd=$param{initrd}\n") if defined $param{initrd}; |
| push (@newkernel, "\troot=$param{root}\n") if defined $param{root}; |
| ##push (@newkernel, "\tread-only\n\n"); |
| |
| if (!defined $param{position} || $param{position} !~ /end|\d+/) { |
| $param{position}=0; |
| } |
| |
| my @newconfig; |
| if ($param{position}=~/end/ || $param{position} >= $#sections) { |
| $param{position}=$#sections; |
| push (@newconfig,@config); |
| if ($newconfig[$#newconfig] =~ /\S/) { |
| push (@newconfig, "\n"); |
| } |
| push (@newconfig,@newkernel); |
| } else { |
| my $index=0; |
| foreach (@config) { |
| if ($_ =~ /^\s*(image|other)/i) { |
| if ($index==$param{position}) { |
| push (@newconfig, @newkernel); |
| } |
| $index++; |
| } |
| push (@newconfig, $_); |
| } |
| } |
| |
| @{$self->{config}} = @newconfig; |
| |
| if (defined $param{'make-default'}) { |
| $self->set_default($param{position}); |
| } |
| } |
| |
| |
| # Update kernel args |
| |
| sub update { |
| my $self=shift; |
| my %params=@_; |
| |
| print ("Updating kernel.\n") if $self->debug()>1; |
| |
| if (!defined $params{'update-kernel'} || (!defined $params{'args'} && !defined $params{'remove-args'})) { |
| warn "ERROR: kernel position or title (--update-kernel) and args (--args or --remove-args) required.\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| my @config = @{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if ($params{'update-kernel'} !~ /^\d+$/) { |
| $params{'update-kernel'} = $self->_lookup($params{'update-kernel'}); |
| } |
| |
| my $kcount = $#sections-1; |
| if ($params{'update-kernel'} !~ /^\d+$/ || $params{'update-kernel'} < 0 || $params{'update-kernel'} > $kcount) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| |
| my $index=-1; |
| foreach (@config) { |
| if ($_ =~ /^\s*(image|other)/i) { |
| $index++; |
| } |
| if ($index==$params{'update-kernel'}) { |
| if ($_ =~ /(^\s*append[\s\=]+)(.*)\n/i) { |
| my $append = $1; |
| my $args = $2; |
| $args =~ s/\"|\'//g; |
| $args = $self->_build_args($args, $params{'remove-args'}, $params{'args'}); |
| if ($_ eq "$append\"$args\"\n") { |
| warn "WARNING: No change made to args.\n"; |
| return undef; |
| } else { |
| $_ = "$append\"$args\"\n"; |
| } |
| next; |
| } |
| } |
| } |
| @{$self->{config}} = @config; |
| } |
| |
| |
| # Remove kernel from config |
| |
| sub remove { |
| my $self=shift; |
| my $position=shift; |
| my @newconfig; |
| |
| return undef unless defined $position; |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| if ($position=~/^end$/i) { |
| $position=$#sections-1; |
| } elsif ($position=~/^start$/i) { |
| $position=0; |
| } |
| |
| print ("Removing kernel $position.\n") if $self->debug()>1; |
| |
| # remove based on title |
| if ($position !~ /^\d+$/) { |
| my $removed=0; |
| for (my $index=$#sections; $index > 0; $index--) { |
| if (defined $sections[$index]{title} && $position eq $sections[$index]{title}) { |
| $removed++ if $self->remove($index-1); |
| } |
| } |
| if (! $removed) { |
| warn "ERROR: No kernel with specified title.\n"; |
| return undef; |
| } |
| |
| # remove based on position |
| } elsif ($position =~ /^\d+$/) { |
| |
| if ($position < 0 || $position > $#sections) { |
| warn "ERROR: Enter a position between 0 and $#sections.\n"; |
| return undef; |
| } |
| |
| my $index=-1; |
| foreach (@config) { |
| if ($_ =~ /^\s*(image|other|title)/i) { |
| $index++ |
| } |
| # add everything to newconfig, except removed kernel (keep comments) |
| if ($index != $position || $_ =~ /^#/) { |
| push (@newconfig, $_) |
| } |
| } |
| @{$self->{config}} = @newconfig; |
| |
| |
| # if we removed the default, set new default to first |
| $self->set_default(0) if $position == $sections[0]{'default'}; |
| |
| print "Removed kernel $position.\n"; |
| return 1; |
| |
| } else { |
| warn "WARNING: problem removing entered position.\n"; |
| return undef; |
| } |
| |
| } |
| |
| |
| # Print info from config |
| |
| sub print_info { |
| my $self=shift; |
| my $info=shift; |
| |
| return undef unless defined $info; |
| return undef unless $self->_check_config(); |
| |
| print ("Printing config info.\n") if $self->debug()>1; |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| my ($start,$end); |
| if ($info =~ /default/i) { |
| $start=$end=$self->get_default() |
| } elsif ($info =~ /all/i) { |
| $start=0; $end=$#sections-1 |
| } elsif ($info =~ /^\d+/) { |
| $start=$end=$info |
| } else { |
| my $index = $self->_lookup($info); |
| if (!defined $index) { |
| warn "ERROR: input should be: #, default, all, or a valid title.\n"; |
| return undef; |
| } |
| $start=$end=$index; |
| } |
| |
| if ($start < 0 || $end > $#sections-1) { |
| warn "ERROR: No kernels with that index.\n"; |
| return undef; |
| } |
| |
| for my $index ($start..$end) { |
| print "\nindex\t: $index\n"; |
| $index++; |
| foreach ( sort keys(%{$sections[$index]}) ) { |
| print "$_\t: $sections[$index]{$_}\n"; |
| } |
| } |
| } |
| |
| |
| # Set/get debug level |
| |
| sub debug { |
| my $self=shift; |
| if (@_) { |
| $self->{debug} = shift; |
| } |
| return $self->{debug} || 0; |
| } |
| |
| # Get a bootloader entry as a hash to edit or update. |
| sub read_entry { |
| my $self=shift; |
| my $entry=shift; |
| |
| if ($entry !~ /^\d+$/) { |
| $entry = $self->_lookup($entry); |
| } |
| my @sections=$self->_info(); |
| |
| my $index = $entry + 1; |
| if ((defined $sections[$index]{'title'})) { |
| $self->{'entry'}->{'index'} = $index; |
| foreach my $key ( keys %{$sections[$index]} ){ |
| $self->{'entry'}->{'data'}->{ $key } = $sections[$index]{$key}; |
| } |
| return $self->{'entry'}->{'data'}; |
| } else { |
| return undef; |
| } |
| } |
| |
| # Basic check for valid config |
| |
| sub _check_config { |
| my $self=shift; |
| |
| print ("Verifying config.\n") if $self->debug()>3; |
| |
| if ($#{$self->{config}} < 5) { |
| warn "ERROR: you must read a valid config file first.\n"; |
| return undef; |
| } |
| return 1; |
| } |
| |
| |
| # lookup position using title |
| |
| sub _lookup { |
| my $self=shift; |
| my $title=shift; |
| |
| unless ( defined $title ){ return undef; } |
| |
| my @sections=$self->_info(); |
| |
| for my $index (1..$#sections) { |
| my $tmp = $sections[$index]{title}; |
| if (defined $tmp and $title eq $tmp) { |
| return $index-1; |
| } |
| } |
| return undef; |
| } |
| |
| sub _build_args { |
| my ($self, $args, $toremove, $toadd) = @_; |
| |
| if (defined $toremove) { |
| my $base; |
| foreach my $remove (split(' ', $toremove)) { |
| $base = $remove; $base =~ s/\=.*//; |
| $args =~ s/(^|\s+)$base(\=\S+|\s+|$)/$1/ig; |
| } |
| } |
| |
| if (defined $toadd) { |
| my $base; |
| foreach my $add (split(' ', $toadd)) { |
| $base = $add; $base =~ s/\=.*//; |
| if (!($args =~ s/(^|\s+)$base(\=\S+)?(\s+|$)/$1$add$3/ig)) { |
| $args .= " $add"; |
| } |
| } |
| } |
| |
| $args =~ s/\s+/ /g; |
| return $args; |
| } |
| |
| |
| =head1 AUTHOR |
| |
| Jason N., Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<boottool>, L<Linux::Bootloader::Grub>, L<Linux::Bootloader::Lilo>, |
| L<Linux::Bootloader::Elilo>, L<Linux::Bootloader::Yaboot> |
| |
| =cut |
| |
| |
| 1; |
| package Linux::Bootloader::Detect; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Detect - detects the bootloader and architecture of the system. |
| |
| =head1 SYNOPSIS |
| |
| Attempts to determine the bootloader by checking for configuration files |
| for grub, lilo, elilo and yaboot then searching the master boot record |
| for GRUB, LILO, ELILO and YABOOT. |
| |
| Determines the architecture by running uname -m. |
| |
| =head1 DESCRIPTION |
| |
| To attempt to discover the bootloader being used by the system |
| detect_bootloader first calls detect_bootloader_from_conf attempts to locate |
| /boot/grub/menu.lst, /etc/lilo.conf, /boot/efi/elilo.conf and |
| /etc/yaboot.conf and returns the corresponding bootloader name. If |
| either undef of multiple are returned because no configuration files or |
| multiple configuration files were found detect_bootloader calls |
| detect_bootloader_from_mbr which generates a list of all devices accessable from |
| the /dev directory reading in the first 512 bytes from each hd and sd |
| device using head then redirects the output to grep to determine if |
| "GRUB", "LILO", "ELILO" or "YABOOT" is present returning the |
| corresponding value if exactly one mbr on the system contained a |
| bootloader or multiple if more than one was found and undef if none were |
| found. detect_bootloader returns either grub, lilo, elilo, yaboot or |
| undef. |
| |
| To attempt to discover the architecture of the system |
| detect_architecture makes a uname -m system call returning x86, ppc, |
| ia64 or undef. |
| |
| =head1 FUNCTIONS |
| |
| =cut |
| |
| use strict; |
| use warnings; |
| |
| use vars qw( $VERSION ); |
| |
| =head3 detect_architecture([style]) |
| |
| Input: |
| Output: string |
| |
| This function determines the architecture by calling uname -m. By |
| default it will report back exactly what uname -m reports, but if you |
| specify a "style", detect_architecture will do some mappings. Possible |
| styles include: |
| |
| Style Example return values (not an exhaustive list...) |
| [none] i386, i686, sparc, sun4u, ppc64, s390x, x86_64, parisc64 |
| linux i386, i386, sparc, sparc, ppc64, s390, x86_64, parisc |
| gentoo x86, x86, sparc, sparc, ppc64, amd64, hppa |
| |
| Returns undef on error. |
| |
| =cut |
| |
| sub detect_architecture { |
| my $arch_style = shift || 'uname'; |
| |
| my $arch; |
| if ($arch_style eq 'linux') { |
| $arch = `uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/`; |
| chomp $arch; |
| } elsif ($arch_style eq 'gentoo') { |
| $arch = `uname -m | sed -e s/i.86/x86/ -e s/sun4u/sparc/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/x86_64/amd64/ -e s/sparc.*/sparc/ -e s/parisc.*/hppa/`; |
| chomp $arch; |
| } else { |
| $arch = `uname -m`; |
| chomp $arch; |
| } |
| return $arch; |
| } |
| |
| =head3 detect_os_vendor() |
| |
| Input: |
| Output: string |
| |
| This function determines the OS vendor (linux distribution breed). |
| |
| Return values: "Red Hat", "Fedora", "SUSE", "Ubuntu", "Debian", or |
| "Unknown" if none of the predefined patterns could be found on the |
| issue file. |
| |
| =cut |
| |
| sub detect_os_vendor { |
| my $vendor = ""; |
| my $issue_file = '/etc/issue'; |
| if ( not system("egrep 'Red Hat' $issue_file") ){ |
| $vendor = 'Red Hat'; |
| } elsif ( not system("egrep 'Fedora' $issue_file") ){ |
| $vendor = 'Fedora'; |
| } elsif ( not system("egrep 'SUSE' $issue_file") ){ |
| $vendor = 'SUSE'; |
| } elsif ( not system("egrep 'Ubuntu' $issue_file") ){ |
| $vendor = 'Ubuntu'; |
| } elsif ( not system("egrep 'Debian' $issue_file") ){ |
| $vendor = 'Debian'; |
| } else { |
| $vendor = 'Unknown'; |
| } |
| return $vendor; |
| } |
| |
| =head3 detect_bootloader(['device1', 'device2', ...]) |
| |
| Input: devices to detect against (optional) |
| Output: string |
| |
| This function attempts to determine the bootloader being used on the |
| system by first checking for conf files and then falling back to check |
| the master boot record. |
| |
| Possible return values: |
| |
| grub grub was determined to be the bootloader in use |
| lilo lilo was determined to be is the bootloader in use |
| elilo elilo was determined to be the bootloader in use |
| yaboot yaboot was determined to be the bootloader in use |
| undef it was impossible to determine which bootloader was being used |
| due either to configuration files for multiple bootloaders or |
| bootloader on multiple hard disks |
| |
| =cut |
| |
| sub detect_bootloader { |
| return detect_bootloader_from_conf(@_) |
| || detect_bootloader_from_mbr(@_); |
| } |
| |
| =head2 detect_bootloader_from_conf() |
| |
| Detects bootloaders by the presence of config files. This is not as |
| reliable of a mechanism as looking in the MBR, but tends to be |
| significantly faster. |
| |
| If called in list context, it will return a list of the bootloaders that |
| it found. |
| |
| If called in scalar context and only a single bootloader config file is |
| present it will return the name of that bootloader. Otherwise, if |
| multiple (or no) bootloaders are detected, it will return undef. |
| |
| =cut |
| |
| sub detect_bootloader_from_conf { |
| my @boot_loader = (); |
| |
| my %boot_list = ( grub => '/boot/grub/menu.lst', |
| lilo => '/etc/lilo.conf', |
| elilo => '/etc/elilo.conf', |
| yaboot => '/etc/yaboot.conf', |
| zipl => '/etc/zipl.conf', |
| ); |
| |
| foreach my $key ( sort keys %boot_list ) { |
| if ( -f $boot_list{$key} ) { |
| push ( @boot_loader, $key ); |
| } |
| } |
| |
| if (wantarray()) { |
| return @boot_loader; |
| } elsif (@boot_loader == 1) { |
| return pop( @boot_loader ); |
| } elsif (@boot_loader == 2) { |
| if ($boot_loader[0] eq 'lilo' && $boot_loader[1] eq 'yaboot') { |
| return 'lilo'; |
| } |
| } |
| |
| if (scalar(@boot_loader) > 1) { |
| warn "Warning: Multiple bootloader configs; not certain which is in use.\n"; |
| warn " " . join(' ', @boot_loader) . "\n"; |
| } |
| return undef; |
| } |
| |
| =head2 detect_bootloader_from_mbr([@devices]) |
| |
| Detects the bootloader by scanning the master boot record (MBR) of the |
| specified devices (or all devices if not indicated). |
| |
| The device arguments must be relative to the /dev/ directory. I.e., |
| ('hda', 'sdb', 'cdroms/cdrom0', etc.) |
| |
| =cut |
| |
| sub detect_bootloader_from_mbr { |
| my @filelist = @_; |
| my @boot_loader = (); |
| |
| my %map = ( |
| "GRUB" => 'grub', |
| "LILO" => 'lilo', |
| "EFI" => 'elilo', |
| "yaboot" => 'yaboot', |
| ); |
| |
| if ( ! @filelist && opendir( DIRH, "/sys/block" ) ) { |
| @filelist = grep { /^[sh]d.$/ } readdir(DIRH); |
| closedir(DIRH); |
| } |
| |
| foreach my $dev ( @filelist ) { |
| if ( -b "/dev/$dev" ) { |
| my $strings = `dd if=/dev/$dev bs=512 count=1 2>/dev/null`; |
| if ($?) { |
| warn "Error: Could not read MBR on /dev/$dev (are you root?)\n"; |
| } else { |
| $strings = `echo $strings | strings`; |
| foreach my $loader (keys %map) { |
| if ($strings =~ /$loader/ms) { |
| push @boot_loader, $map{$loader}; |
| } |
| } |
| } |
| } |
| } |
| |
| if (wantarray()) { |
| # Show them all |
| return @boot_loader; |
| } elsif (@boot_loader == 1) { |
| # Found exactly one |
| return pop @boot_loader; |
| } elsif (@boot_loader == 2) { |
| # This is the Lilo/Grub exception |
| # Grub on MBR with previous Lilo install |
| # Are they lilo and grub in that order? |
| if ($boot_loader[0] eq 'lilo' and $boot_loader[1] eq 'grub'){ |
| warn "Warning: Grub appears to be used currently, but Lilo was in past.\n"; |
| return $boot_loader[1]; |
| } |
| } else { |
| warn "Warning: Multiple MBR's present; not certain which is in use.\n"; |
| warn " " . join(' ', @boot_loader) . "\n"; |
| return undef; |
| } |
| |
| # Either none or too many to choose from |
| return undef; |
| } |
| |
| 1; |
| |
| =head1 AUTHOR |
| |
| Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| package Linux::Bootloader::Elilo; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Elilo - Parse and modify ELILO configuration files. |
| |
| =head1 SYNOPSIS |
| |
| |
| my $bootloader = Linux::Bootloader::Elilo->new(); |
| my $config_file='/etc/elilo.conf'; |
| |
| $bootloader->read($config_file) |
| |
| # add a kernel |
| $bootloader->add(%hash) |
| |
| # remove a kernel |
| $bootloader->remove(2) |
| |
| # set new default |
| $bootloader->set_default(1) |
| |
| $bootloader->write($config_file) |
| |
| |
| =head1 DESCRIPTION |
| |
| This module provides functions for working with ELILO configuration files. |
| |
| Adding a kernel: |
| - add kernel at start, end, or any index position. |
| - kernel path and title are required. |
| - root, kernel args, initrd are optional. |
| - any options not specified are copied from default. |
| - remove any conflicting kernels if force is specified. |
| |
| Removing a kernel: |
| - remove by index position |
| - or by title/label |
| |
| |
| =head1 FUNCTIONS |
| |
| Also see L<Linux::Bootloader> for functions available from the base class. |
| |
| =head2 new() |
| |
| Creates a new Linux::Bootloader::Elilo object. |
| |
| =head2 install() |
| |
| Attempts to install bootloader. |
| Takes: nothing. |
| Returns: undef on error. |
| |
| =cut |
| |
| |
| use strict; |
| use warnings; |
| |
| @Linux::Bootloader::Elilo::ISA = qw(Linux::Bootloader); |
| use base 'Linux::Bootloader'; |
| |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub _set_config_file { |
| my $self=shift; |
| $self->{'config_file'}='/etc/elilo.conf'; |
| } |
| |
| |
| ### ELILO functions ### |
| |
| |
| # Run command to install bootloader |
| |
| sub install { |
| my $self=shift; |
| |
| my $elilo = ''; |
| $elilo = '/sbin/elilo' if (-f '/sbin/elilo'); |
| $elilo = '/usr/sbin/elilo' if (-f '/usr/sbin/elilo'); |
| if ($elilo ne '') { |
| system($elilo); |
| if ($? != 0) { |
| warn ("ERROR: Failed to run elilo.\n") && return undef; |
| } |
| } |
| return 1; |
| } |
| |
| # Set kernel to be booted once |
| |
| sub boot_once { |
| my $self=shift; |
| my $label = shift; |
| |
| return undef unless defined $label; |
| |
| $self->read( '/etc/elilo.conf' ); |
| my @config=@{$self->{config}}; |
| |
| if ( ! grep( /^checkalt/i, @config ) ) { |
| warn("ERROR: Failed to set boot-once.\n"); |
| warn("Please add 'checkalt' to global config.\n"); |
| return undef; |
| } |
| |
| my @sections = $self->_info(); |
| my $position = $self->_lookup($label); |
| $position++; |
| my $efiroot = `grep ^EFIROOT /usr/sbin/elilo | cut -d '=' -f 2`; |
| chomp($efiroot); |
| |
| my $kernel = $efiroot . $sections[$position]{kernel}; |
| my $root = $sections[$position]{root}; |
| my $args = $sections[$position]{args}; |
| |
| #system( "/usr/sbin/eliloalt", "-d" ); |
| if ( system( "/usr/sbin/eliloalt", "-s", "$kernel root=$root $args" ) ) { |
| warn("ERROR: Failed to set boot-once.\n"); |
| warn("1) Check that EFI var support is compiled into kernel.\n"); |
| warn("2) Verify eliloalt works. You may need to patch it to support sysfs EFI vars.\n"); |
| return undef; |
| } |
| return 1; |
| } |
| |
| |
| 1; |
| |
| |
| =head1 AUTHOR |
| |
| Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| |
| package Linux::Bootloader::Grub; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Grub - Parse and modify GRUB configuration files. |
| |
| =head1 SYNOPSIS |
| |
| |
| my $config_file='/boot/grub/menu.lst'; |
| $bootloader = Linux::Bootloader::Grub->new($config_file); |
| |
| $bootloader->read(); |
| |
| # add a kernel |
| $bootloader->add(%hash) |
| |
| # remove a kernel |
| $bootloader->remove(2) |
| |
| # print config info |
| $bootloader->print_info('all') |
| |
| # set new default |
| $bootloader->set_default(1) |
| |
| $bootloader->write(); |
| |
| |
| =head1 DESCRIPTION |
| |
| This module provides functions for working with GRUB configuration files. |
| |
| Adding a kernel: |
| - add kernel at start, end, or any index position. |
| - kernel path and title are required. |
| - root, kernel args, initrd, savedefault, module are optional. |
| - any options not specified are copied from default. |
| - remove any conflicting kernels first if force is specified. |
| |
| Removing a kernel: |
| - remove by index position |
| - or by title/label |
| |
| |
| =head1 FUNCTIONS |
| |
| Also see L<Linux::Bootloader> for functions available from the base class. |
| |
| =head2 new() |
| |
| Creates a new Linux::Bootloader::Grub object. |
| |
| =head2 _info() |
| |
| Parse config into array of hashes. |
| Takes: nothing. |
| Returns: array of hashes containing config file options and boot entries, |
| undef on error. |
| |
| =head2 set_default() |
| |
| Set new default kernel. |
| Takes: integer or string, boot menu position or title. |
| Returns: undef on error. |
| |
| =head2 add() |
| |
| Add new kernel to config. |
| Takes: hash containing kernel path, title, etc. |
| Returns: undef on error. |
| |
| =head2 update() |
| |
| Update args of an existing kernel entry. |
| Takes: hash containing args and entry to update. |
| Returns: undef on error. |
| |
| =head2 install() |
| |
| Prints message on how to re-install grub. |
| Takes: nothing. |
| Returns: nothing. |
| |
| =head2 update_main_options() |
| |
| This updates or adds a general line anywhere before the first 'title' line. |
| it is called with the 'update' and 'option' options, when no 'update-kernel' |
| is specified. |
| |
| =head2 boot_once() |
| |
| This is a special case of using 'fallback'. This function makes the current |
| default the fallback kernel and sets the passed argument to be the default |
| kernel which saves to the fallback kernel after booting. The file |
| '/boot/grub/default' is created if it does not exist. |
| |
| This only works with grub versions 0.97 or better. |
| |
| =head2 _get_bootloader_version() |
| |
| Prints detected grub version. |
| Takes: nothing. |
| Returns: nothing. |
| |
| =cut |
| |
| use strict; |
| use warnings; |
| |
| @Linux::Bootloader::Grub::ISA = qw(Linux::Bootloader); |
| use base 'Linux::Bootloader'; |
| |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub _set_config_file { |
| my $self=shift; |
| $self->{'config_file'}='/boot/grub/menu.lst'; |
| } |
| |
| |
| ### GRUB functions ### |
| |
| # Parse config into array of hashes |
| |
| sub _info { |
| my $self=shift; |
| |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| @config=grep(!/^#|^\n/, @config); |
| |
| my %matches = ( default => '^\s*default\s*\=*\s*(\S+)', |
| timeout => '^\s*timeout\s*\=*\s*(\S+)', |
| fallback => '^\s*fallback\s*\=*\s*(\S+)', |
| kernel => '^\s*kernel\s+(\S+)', |
| root => '^\s*kernel\s+.*\s+.*root=(\S+)', |
| args => '^\s*kernel\s+\S+\s+(.*)\n', |
| boot => '^\s*root\s+(.*)', |
| initrd => '^\s*initrd\s+(.*)', |
| savedefault => '^\s*savedefault\s+(.*)', |
| module => '^\s*module\s+(.+)', |
| ); |
| |
| my @sections; |
| my $index=0; |
| foreach (@config) { |
| if ($_ =~ /^\s*title\s+(.*)/i) { |
| $index++; |
| $sections[$index]{title} = $1; |
| } |
| foreach my $key (keys %matches) { |
| if ($_ =~ /$matches{$key}/i) { |
| $key .= '2' if exists $sections[$index]{$key}; |
| $sections[$index]{$key} = $1; |
| if ($key eq 'args') { |
| $sections[$index]{$key} =~ s/root=\S+\s*//i; |
| delete $sections[$index]{$key} if ($sections[$index]{$key} !~ /\S/); |
| } |
| } |
| } |
| } |
| |
| # sometimes config doesn't have a default, so goes to first |
| if (!(defined $sections[0]{'default'})) { |
| $sections[0]{'default'} = '0'; |
| |
| # if default is 'saved', read from grub default file |
| } elsif ($sections[0]{'default'} =~ m/^saved$/i) { |
| open(DEFAULT_FILE, '/boot/grub/default') |
| || warn ("ERROR: cannot read grub default file.\n") && return undef; |
| my @default_config = <DEFAULT_FILE>; |
| close(DEFAULT_FILE); |
| $default_config[0] =~ /^(\d+)/; |
| $sections[0]{'default'} = $1; |
| } |
| |
| # return array of hashes |
| return @sections; |
| } |
| |
| |
| # Set new default kernel |
| |
| sub set_default { |
| my $self=shift; |
| my $newdefault=shift; |
| |
| return undef unless defined $newdefault; |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if ($newdefault !~ /^\d+$/ && $newdefault !~ m/^saved$/) { |
| $newdefault = $self->_lookup($newdefault); |
| return undef unless (defined $newdefault); |
| } |
| |
| my $kcount = $#sections-1; |
| if ($newdefault !~ m/saved/) { |
| if (($newdefault < 0) || ($newdefault > $kcount)) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| } |
| |
| foreach my $index (0..$#config) { |
| |
| if ($config[$index] =~ /(^\s*default\s*\=*\s*)\d+/i) { |
| $config[$index] = "$1$newdefault\n"; |
| last; |
| } elsif ($config[$index] =~ /^\s*default\s*\=*\s*saved/i) { |
| my @default_config; |
| my $default_config_file='/boot/grub/default'; |
| |
| open(DEFAULT_FILE, $default_config_file) |
| || warn ("ERROR: cannot open default file.\n") && return undef; |
| @default_config = <DEFAULT_FILE>; |
| close(DEFAULT_FILE); |
| |
| if ($newdefault eq 'saved') { |
| warn "WARNING: Setting new default to '0'\n"; |
| $newdefault = 0; |
| } |
| |
| $default_config[0] = "$newdefault\n"; |
| |
| open(DEFAULT_FILE, ">$default_config_file") |
| || warn ("ERROR: cannot open default file.\n") && return undef; |
| print DEFAULT_FILE join("",@default_config); |
| close(DEFAULT_FILE); |
| last; |
| } |
| } |
| @{$self->{config}} = @config; |
| } |
| |
| |
| # Add new kernel to config |
| |
| sub add { |
| my $self=shift; |
| my %param=@_; |
| |
| print ("Adding kernel.\n") if $self->debug()>1; |
| |
| if (!defined $param{'add-kernel'} || !defined $param{'title'}) { |
| warn "ERROR: kernel path (--add-kernel), title (--title) required.\n"; |
| return undef; |
| } elsif (!(-f "$param{'add-kernel'}")) { |
| warn "ERROR: kernel $param{'add-kernel'} not found!\n"; |
| return undef; |
| } elsif (defined $param{'initrd'} && !(-f "$param{'initrd'}")) { |
| warn "ERROR: initrd $param{'initrd'} not found!\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| my @sections=$self->_info(); |
| |
| # check if title already exists |
| if (defined $self->_lookup($param{title})) { |
| warn ("WARNING: Title already exists.\n"); |
| if (defined $param{force}) { |
| $self->remove($param{title}); |
| } else { |
| return undef; |
| } |
| } |
| |
| my @config = @{$self->{config}}; |
| @sections=$self->_info(); |
| |
| # Use default kernel to fill in missing info |
| my $default=$self->get_template(); |
| $default++; |
| |
| foreach my $p ('args', 'root', 'boot', 'savedefault') { |
| if (! defined $param{$p}) { |
| $param{$p} = $sections[$default]{$p}; |
| } |
| } |
| |
| # use default entry to determine if path (/boot) should be removed |
| my $bootpath = $sections[$default]{'kernel'}; |
| $bootpath =~ s@[^/]*$@@; |
| |
| $param{'add-kernel'} =~ s@^/boot/@$bootpath@; |
| $param{'initrd'} =~ s@^/boot/@$bootpath@ unless !defined $param{'initrd'}; |
| |
| my @newkernel; |
| push(@newkernel, "title\t$param{title}\n") if defined $param{title}; |
| push(@newkernel, "\troot $param{boot}\n") if defined $param{boot}; |
| |
| my $line; |
| if ( defined $param{xen} ) { |
| $line = "\tkernel $sections[$default]{kernel}"; |
| $line .= " $sections[$default]{root}" if defined $sections[$default]{root}; |
| $line .= " $sections[$default]{args}" if defined $sections[$default]{args}; |
| push( @newkernel, "$line\n" ); |
| push( @newkernel, "\tinitrd $sections[$default]{'initrd'}\n" ) if defined $sections[$default]{'initrd'}; |
| $line = "\tmodule $param{'add-kernel'}" if defined $param{'add-kernel'}; |
| $line .= " root=$param{root}" if defined $param{root}; |
| $line .= " $param{args}" if defined $param{args}; |
| push( @newkernel, "$line\n" ); |
| push( @newkernel, "\tmodule $param{initrd}\n" ) if defined $param{initrd}; |
| } else { |
| $line = "\tkernel $param{'add-kernel'}" if defined $param{'add-kernel'}; |
| $line .= " root=$param{root}" if defined $param{root}; |
| $line .= " $param{args}" if defined $param{args}; |
| push( @newkernel, "$line\n" ); |
| push( @newkernel, "\tinitrd $param{initrd}\n" ) if defined $param{initrd}; |
| } |
| |
| push(@newkernel, "\tsavedefault $param{savedefault}\n") if defined $param{savedefault}; |
| |
| foreach my $module (@{$param{'module'}}) { |
| push(@newkernel, "\tmodule " . $module . "\n"); |
| } |
| |
| push(@newkernel, "\n"); |
| |
| if (!defined $param{position} || $param{position} !~ /end|\d+/) { |
| $param{position}=0 |
| } |
| |
| my @newconfig; |
| if ($param{position}=~/end/ || $param{position} >= $#sections) { |
| $param{position}=$#sections; |
| push (@newconfig,@config); |
| if ($newconfig[$#newconfig] =~ /\S/) { |
| push (@newconfig, "\n"); |
| } |
| push (@newconfig,@newkernel); |
| } else { |
| my $index=0; |
| foreach (@config) { |
| if ($_ =~ /^\s*title/i) { |
| if ($index==$param{position}) { |
| push (@newconfig, @newkernel); |
| } |
| $index++; |
| } |
| push (@newconfig, $_); |
| } |
| } |
| |
| @{$self->{config}} = @newconfig; |
| |
| if (defined $param{'make-default'} || defined $param{'boot-once'}) { |
| $self->set_default($param{position}); |
| } |
| print "Added: $param{'title'}.\n"; |
| } |
| |
| |
| # Update kernel args |
| |
| sub update { |
| my $self=shift; |
| my %params=@_; |
| |
| print ("Updating kernel.\n") if $self->debug()>1; |
| |
| if (defined $params{'option'} && !defined $params{'update-kernel'}) { |
| return $self->update_main_options(%params); |
| } elsif (!defined $params{'update-kernel'} || (!defined $params{'args'} && !defined $params{'remove-args'} && !defined $params{'option'})) { |
| warn "ERROR: kernel position or title (--update-kernel) and args (--args or --remove-args) required.\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| # my @config = @{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if (defined $params{'update-kernel'} and $params{'update-kernel'} !~ /^\d+$/) { |
| $params{'update-kernel'} = $self->_lookup($params{'update-kernel'}); |
| } |
| |
| my $kcount = $#sections-1; |
| if ($params{'update-kernel'} !~ /^\d+$/ || $params{'update-kernel'} < 0 || $params{'update-kernel'} > $kcount) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| |
| my $kregex = '(^\s*kernel\s+\S+)(.*)'; |
| $kregex = '(^\s*module\s+\S+vmlinuz\S+)(.*)' if defined $params{'xen'}; |
| |
| my $index=-1; |
| my $config_line = -1; |
| my $line = ''; |
| foreach $line (@{$self->{config}}) { |
| $config_line = $config_line + 1; |
| if ($line =~ /^\s*title/i) { |
| $index++; |
| } |
| if ($index==$params{'update-kernel'}) { |
| if (defined $params{'args'} or defined $params{'remove-args'}){ |
| if ( $line =~ /$kregex/i ) { |
| my $kernel = $1; |
| my $args = $self->_build_args($2, $params{'remove-args'}, $params{'args'}); |
| if ($line eq $kernel . $args . "\n") { |
| warn "WARNING: No change made to args.\n"; |
| return undef; |
| } else { |
| $line = $kernel . $args . "\n"; |
| } |
| next; |
| } |
| } elsif (defined $params{'option'}){ |
| foreach my $val ( keys %params){ |
| if ($line =~ m/^\s*$val.*/i) { |
| splice @{$self->{config}},$config_line,1,"$val $params{$val}\n"; |
| delete $params{$val}; |
| $config_line += 1; |
| } |
| } |
| } |
| } elsif ($index > $params{'update-kernel'}){ |
| last; |
| } |
| } |
| # Add any leftover parameters |
| delete $params{'update-kernel'}; |
| if (defined $params{'option'}){ |
| delete $params{'option'}; |
| $config_line -= 1; |
| foreach my $val ( keys %params){ |
| splice @{$self->{config}},$config_line,0,"$val $params{$val}\n"; |
| $config_line += 1; |
| } |
| } |
| } |
| |
| |
| # Run command to install bootloader |
| |
| sub install { |
| my $self=shift; |
| my $device; |
| |
| warn "Re-installing grub is currently unsupported.\n"; |
| warn "If you really need to re-install grub, use 'grub-install <device>'.\n"; |
| return undef; |
| |
| #system("grub-install $device"); |
| #if ($? != 0) { |
| # warn ("ERROR: Failed to run grub-install.\n") && return undef; |
| #} |
| #return 1; |
| } |
| |
| |
| sub update_main_options{ |
| my $self=shift; |
| my %params=@_; |
| delete $params{'option'}; |
| foreach my $val (keys %params){ |
| my $x=0; |
| foreach my $line ( @{$self->{config}} ) { |
| # Replace |
| if ($line =~ m/^\s*$val/) { |
| splice (@{$self->{config}},$x,1,"$val $params{$val}\n"); |
| last; |
| } |
| # Add |
| if ($line =~ /^\s*title/i) { |
| # This is a new option, add it before here |
| print "Your option is not in current configuration. Adding.\n"; |
| splice @{$self->{config}},$x,0,"$val $params{$val}\n"; |
| last; |
| } |
| $x+=1; |
| } |
| } |
| } |
| |
| |
| sub boot_once { |
| my $self=shift; |
| my $entry_to_boot_once = shift; |
| my $detected_os_vendor = Linux::Bootloader::Detect::detect_os_vendor(); |
| |
| unless ( $entry_to_boot_once ) { print "No kernel\n"; return undef;} |
| $self->read(); |
| my $default=$self->get_default(); |
| |
| if ( $self->_get_bootloader_version() < 0.97 ){ |
| warn "This function works for grub version 0.97 and up. No action taken. \nUpgrade, then re-try.\n"; |
| return undef; |
| } |
| |
| if ( $detected_os_vendor eq "Red Hat" or $detected_os_vendor eq "Fedora" ) { |
| # if not a number, do title lookup |
| if ( $entry_to_boot_once !~ /^\d+$/ ) { |
| $entry_to_boot_once = $self->_lookup($entry_to_boot_once); |
| return undef unless ( defined $entry_to_boot_once ); |
| } |
| |
| return `echo "savedefault --default=$entry_to_boot_once" --once | grub --batch`; |
| } else { |
| if ( $default == $self->_lookup($entry_to_boot_once)){ |
| warn "The default and once-boot kernels are the same. No action taken. \nSet default to something else, then re-try.\n"; |
| return undef; |
| } |
| |
| $self->set_default('saved'); |
| if ( ! -f '/boot/grub/default' ){ |
| open FH, '>/boot/grub/default'; |
| my $file_contents="default |
| # |
| # |
| # |
| # |
| # |
| # |
| # |
| # |
| # |
| # |
| # WARNING: If you want to edit this file directly, do not remove any line |
| # from this file, including this warning. Using `grub-set-default\' is |
| # strongly recommended. |
| "; |
| print FH $file_contents; |
| close FH; |
| } |
| $self->set_default( "$entry_to_boot_once" ); |
| $self->update( 'option'=>'','fallback' => $default ); |
| $self->update( 'update-kernel'=>"$entry_to_boot_once",'option'=>'','savedefault' => 'fallback' ); |
| $self->update( 'update-kernel'=>"$default",'option'=>'', 'savedefault' => '' ); |
| $self->write(); |
| } |
| } |
| |
| sub _get_bootloader_version { |
| my $self = shift; |
| return `grub --version | sed 's/grub (GNU GRUB //' | sed 's/)//'`; |
| } |
| |
| |
| 1; |
| |
| |
| =head1 AUTHOR |
| |
| Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| package Linux::Bootloader::Lilo; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Lilo - Parse and modify LILO configuration files. |
| |
| =head1 SYNOPSIS |
| |
| |
| my $bootloader = Linux::Bootloader::Lilo->new(); |
| my $config_file='/etc/lilo.conf'; |
| |
| $bootloader->read($config_file) |
| |
| # add a kernel |
| $bootloader->add(%hash) |
| |
| # remove a kernel |
| $bootloader->remove(2) |
| |
| # set new default |
| $bootloader->set_default(1) |
| |
| $bootloader->write($config_file) |
| |
| |
| =head1 DESCRIPTION |
| |
| This module provides functions for working with LILO configuration files. |
| |
| Adding a kernel: |
| - add kernel at start, end, or any index position. |
| - kernel path and title are required. |
| - root, kernel args, initrd are optional. |
| - any options not specified are copied from default. |
| - remove any conflicting kernels if force is specified. |
| |
| Removing a kernel: |
| - remove by index position |
| - or by title/label |
| |
| |
| =head1 FUNCTIONS |
| |
| Also see L<Linux::Bootloader> for functions available from the base class. |
| |
| =head2 new() |
| |
| Creates a new Linux::Bootloader::Lilo object. |
| |
| =head2 install() |
| |
| Attempts to install bootloader. |
| Takes: nothing. |
| Returns: undef on error. |
| |
| =head2 boot-once() |
| |
| Attempts to set a kernel as default for one boot only. |
| Takes: string. |
| Returns: undef on error. |
| |
| =cut |
| |
| |
| use strict; |
| use warnings; |
| |
| @Linux::Bootloader::Lilo::ISA = qw(Linux::Bootloader); |
| use base 'Linux::Bootloader'; |
| |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub _set_config_file { |
| my $self=shift; |
| $self->{'config_file'}='/etc/lilo.conf'; |
| } |
| |
| |
| |
| ### LILO functions ### |
| |
| |
| # Run command to install bootloader |
| |
| sub install { |
| my $self=shift; |
| |
| system("/sbin/lilo"); |
| if ($? != 0) { |
| warn ("ERROR: Failed to run lilo.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| |
| # Set kernel to be booted once |
| |
| sub boot_once { |
| my $self=shift; |
| my $label=shift; |
| |
| return undef unless defined $label; |
| |
| if (system("/sbin/lilo","-R","$label")) { |
| warn ("ERROR: Failed to set boot-once.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| |
| 1; |
| |
| |
| =head1 AUTHOR |
| |
| Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| package Linux::Bootloader::Yaboot; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Yaboot - Parse and modify YABOOT configuration files. |
| |
| =head1 SYNOPSIS |
| |
| |
| my $bootloader = Linux::Bootloader::Yaboot->new(); |
| my $config_file='/etc/yaboot.conf'; |
| |
| $bootloader->read($config_file) |
| |
| # add a kernel |
| $bootloader->add(%hash) |
| |
| # remove a kernel |
| $bootloader->remove(2) |
| |
| # set new default |
| $bootloader->set_default(1) |
| |
| $bootloader->write($config_file) |
| |
| |
| =head1 DESCRIPTION |
| |
| This module provides functions for working with YABOOT configuration files. |
| |
| Adding a kernel: |
| - add kernel at start, end, or any index position. |
| - kernel path and title are required. |
| - root, kernel args, initrd are optional. |
| - any options not specified are copied from default. |
| - remove any conflicting kernels if force is specified. |
| |
| Removing a kernel: |
| - remove by index position |
| - or by title/label |
| |
| |
| =head1 FUNCTIONS |
| |
| Also see L<Linux::Bootloader> for functions available from the base class. |
| |
| =head2 new() |
| |
| Creates a new Linux::Bootloader::Yaboot object. |
| |
| =head2 install() |
| |
| Attempts to install bootloader. |
| Takes: nothing. |
| Returns: undef on error. |
| |
| =cut |
| |
| |
| use strict; |
| use warnings; |
| |
| @Linux::Bootloader::Yaboot::ISA = qw(Linux::Bootloader); |
| use base 'Linux::Bootloader'; |
| |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub _set_config_file { |
| my $self=shift; |
| $self->{'config_file'}='/etc/yaboot.conf'; |
| } |
| |
| ### YABOOT functions ### |
| |
| |
| # Run command to install bootloader |
| |
| sub install { |
| my $self=shift; |
| my $cmd=""; |
| # ybin currently returns an error even when it succeeds, but by |
| # dumb luck ybin -v does the right thing |
| if (-f "/usr/sbin/ybin") { |
| $cmd="/usr/sbin/ybin -v > /dev/null"; |
| } elsif (-f "/sbin/ybin") { |
| $cmd="/sbin/ybin -v > /dev/null"; |
| } else { |
| print("Not installing bootloader.\n"); |
| } |
| |
| system($cmd); |
| if ( $? != 0 ) { |
| warn("ERROR: Failed to run ybin.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| |
| 1; |
| |
| |
| =head1 AUTHOR |
| |
| IBM, Linux Technology Centre, Andy Whitcroft <apw@uk.ibm.com> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 IBM Corperation |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| package Linux::Bootloader::Zipl; |
| |
| =head1 NAME |
| |
| Linux::Bootloader::Zipl - Parse and modify ZIPL configuration files. |
| |
| =cut |
| |
| use strict; |
| use warnings; |
| |
| @Linux::Bootloader::Zipl::ISA = qw(Linux::Bootloader); |
| use base 'Linux::Bootloader'; |
| |
| |
| use vars qw( $VERSION ); |
| |
| |
| sub _set_config_file { |
| my $self=shift; |
| $self->{'config_file'}='/etc/zipl.conf'; |
| } |
| |
| |
| ### ZIPL functions ### |
| |
| # Parse config into array of hashes |
| sub _info { |
| my $self=shift; |
| |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| @config=grep(!/^#|^\s*$/, @config); |
| |
| my %matches = ( |
| target => '^\s*target\s*=\s*(.*)', |
| kernel => '^\s*image\s*=\s*(\S+)', |
| initrd => '^\s*ramdisk\s*=\s*(.*)', |
| args => '^\s*parameters\s*=\s*"?\s*(.*[^"])"?', |
| ); |
| |
| my %sect_title; |
| my $menu_name; |
| my $title; |
| my @sections; |
| foreach (@config) { |
| chomp($_); |
| |
| # Note the menu and switch mode. |
| if ($_ =~ /^:(menu\S*)/) { |
| $menu_name = $1; |
| |
| # An entry starts [name] |
| } elsif ($_ =~ /^\s*\[(\S+)\]/i) { |
| $title = $1; |
| $sect_title{$title}{title} = $title; |
| } |
| |
| # Decode the entry fields |
| if (!defined $menu_name) { |
| foreach my $key (keys %matches) { |
| if ($_ =~ /$matches{$key}/i) { |
| $key .= '2' if exists $sect_title{$title}{$key}; |
| $sect_title{$title}{$key} = $1; |
| } |
| } |
| |
| # This is the menu, pull it in |
| } else { |
| # If this is an entry specified copy entry in to the result. |
| if ($_ =~ /^\s+(\d+)\s*=\s*(\S*)/) { |
| $sections[$1] = $sect_title{$2}; |
| |
| # record all the other attributes here, pick out the default |
| # if we see it. |
| } else { |
| if ($_ =~ /^\s+(\S+)\s*=\s*(.*\S)\s*/) { |
| $sections[0]{$1} = $2; |
| } |
| } |
| } |
| } |
| $sections[0]{'menu'} = $menu_name; |
| if (defined $sections[0]{'default'}) { |
| $sections[0]{'default'}--; |
| } |
| |
| # sometimes config doesn't have a default, so goes to first |
| if (!(defined $sections[0]{'default'})) { |
| $sections[0]{'default'} = '0'; |
| |
| # if default is label name, we need position |
| } elsif ($sections[0]{'default'} !~ m/^\d+$/) { |
| foreach my $index (1..$#sections) { |
| if ($sections[$index]{'title'} eq $sections[0]{'default'}) { |
| $sections[0]{'default'} = $index-1; |
| last; |
| } |
| } |
| $sections[0]{'default'} = 0 if (!defined $sections[0]{'default'}); |
| } |
| |
| # return array of hashes |
| return @sections; |
| } |
| |
| # Set new default kernel |
| |
| sub set_default { |
| my $self=shift; |
| my $newdefault=shift; |
| |
| return undef unless defined $newdefault; |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if ($newdefault !~ /^\d+$/) { |
| $newdefault = $self->_lookup($newdefault); |
| return undef unless (defined $newdefault); |
| } |
| |
| my $kcount = $#sections-1; |
| if (($newdefault < 0) || ($newdefault > $kcount)) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| |
| # Look up the actual title of this section. |
| my $title = $sections[$newdefault + 1]{'title'}; |
| |
| # Look through the config file for the specifier, |
| # note there are two, one the name and one the number |
| # go figure. Note that ZIPL numbering is 1..N. |
| foreach my $index (0..$#config) { |
| if ($config[$index] =~ /(^\s*default\s*\=*\s*)\d+\s*$/i) { |
| $config[$index] = $1 . ($newdefault + 1) . "\n"; |
| |
| } elsif ($config[$index] =~ /(^\s*default\s*\=*\s*)/i) { |
| $config[$index] = "$1$title\n"; |
| } |
| } |
| @{$self->{config}} = @config; |
| } |
| |
| |
| # Add new kernel to config |
| sub add { |
| my $self=shift; |
| my %param=@_; |
| |
| print ("Adding kernel.\n") if $self->debug()>1; |
| |
| if (!defined $param{'add-kernel'} || !defined $param{'title'}) { |
| warn "ERROR: kernel path (--add-kernel), title (--title) required.\n"; |
| return undef; |
| } elsif (!(-f "$param{'add-kernel'}")) { |
| warn "ERROR: kernel $param{'add-kernel'} not found!\n"; |
| return undef; |
| } elsif (defined $param{'initrd'} && !(-f "$param{'initrd'}")) { |
| warn "ERROR: initrd $param{'initrd'} not found!\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| my @sections=$self->_info(); |
| |
| # check if title already exists |
| if (defined $self->_lookup($param{title})) { |
| warn ("WARNING: Title already exists.\n"); |
| if (defined $param{force}) { |
| $self->remove($param{title}); |
| } else { |
| return undef; |
| } |
| } |
| |
| my @config = @{$self->{config}}; |
| @sections=$self->_info(); |
| |
| # Use default kernel to fill in missing info |
| my $default=$self->get_template(); |
| $default++; |
| |
| foreach my $p ('args', 'target') { |
| if (! defined $param{$p}) { |
| $param{$p} = $sections[$default]{$p}; |
| } |
| } |
| |
| # use default entry to determine if path (/boot) should be removed |
| my $bootpath = $sections[$default]{'kernel'}; |
| $bootpath =~ s@[^/]*$@@; |
| |
| $param{'add-kernel'} =~ s@^/boot/@$bootpath@; |
| $param{'initrd'} =~ s@^/boot/@$bootpath@ unless !defined $param{'initrd'}; |
| |
| my $line; |
| my @newkernel; |
| push(@newkernel, "[$param{'title'}]\n"); |
| push(@newkernel, "\ttarget=$param{'target'}\n") if (defined $param{'target'}); |
| push(@newkernel, "\timage=$param{'add-kernel'}\n"); |
| push(@newkernel, "\tramdisk=$param{'initrd'}\n") if (defined $param{'initrd'}); |
| $line = ''; |
| $line .= "root=$param{root} " if (defined $param{'root'}); |
| $line .= "$param{args} " if (defined $param{'args'}); |
| chop($line); |
| push(@newkernel, "\tparameters=\"$line\"\n"); |
| |
| push(@newkernel, "\n"); |
| |
| if (!defined $param{position} || $param{position} !~ /end|\d+/) { |
| $param{position} = 0; |
| } |
| |
| my @newconfig; |
| my $index=0; |
| my $menu=0; |
| my @list; |
| foreach (@config) { |
| if ($_ !~ /^\s*\[defaultboot]/i && $_ =~ /^\s*\[(\S+)]/i) { |
| if ($param{'position'} ne 'end' && $index == $param{position}) { |
| push(@newconfig, @newkernel); |
| push(@list, $param{'title'}); |
| } |
| $index++; |
| push(@list, $1); |
| |
| } elsif (/^:menu\S*/) { |
| if ($param{'position'} eq 'end' || $index < $param{'position'}) { |
| push(@newconfig, @newkernel); |
| push(@list, $param{'title'}); |
| $param{position} = $index; |
| } |
| # Rebuild the menu entries. |
| push(@newconfig, $_); |
| for (my $n = 0; $n <= $#list; $n++) { |
| push(@newconfig, "\t" . ($n+1) . "=$list[$n]\n"); |
| } |
| $menu = 1; |
| next; |
| } |
| if ($menu) { |
| if (/^\s+\d+=/) { |
| next; |
| } else { |
| $menu = 0; |
| } |
| } |
| push(@newconfig, $_); |
| } |
| |
| @{$self->{config}} = @newconfig; |
| |
| if (defined $param{'make-default'} || defined $param{'boot-once'}) { |
| $self->set_default($param{position}); |
| } |
| print "Added: $param{'title'}.\n"; |
| } |
| |
| |
| # Remove a kernel from config |
| sub remove { |
| my $self=shift; |
| my $position=shift; |
| |
| return undef unless defined $position; |
| return undef unless $self->_check_config(); |
| |
| my @config=@{$self->{config}}; |
| my @sections=$self->_info(); |
| my $default = $self->get_default(); |
| |
| if ($position=~/^end$/i) { |
| $position=$#sections-1; |
| } elsif ($position=~/^start$/i) { |
| $position=0; |
| } |
| |
| print ("Removing kernel $position.\n") if $self->debug()>1; |
| |
| # if not a number, do title lookup |
| if ($position !~ /^\d+$/) { |
| $position = $self->_lookup($position); |
| } |
| if ($position !~ /^\d+$/) { |
| warn "ERROR: $position: should be # or title\n"; |
| return undef; |
| } |
| |
| my $title = $sections[$position + 1]{'title'}; |
| |
| my $keep = 1; |
| my @newconfig; |
| my @list; |
| my $index = 0; |
| my $menu; |
| foreach (@config) { |
| if ($_ !~ /^\s*\[defaultboot]/i && $_ =~ /^\s*\[(\S+)]/i) { |
| if ($index == $position) { |
| $keep = 0; |
| } else { |
| push(@list, $1); |
| $keep = 1; |
| } |
| $index++; |
| |
| } elsif (/^:menu\S*/) { |
| # Rebuild the menu entries. |
| push(@newconfig, $_); |
| for (my $n = 0; $n <= $#list; $n++) { |
| push(@newconfig, "\t" . ($n+1) . "=$list[$n]\n"); |
| } |
| $menu = 1; |
| $keep = 1; |
| next; |
| } |
| if ($menu) { |
| if (/^\s+\d+=/) { |
| next; |
| } else { |
| $menu = 0; |
| } |
| } |
| push(@newconfig, $_) if ($keep); |
| } |
| |
| @{$self->{config}} = @newconfig; |
| |
| # Update the default. |
| my $new = $default; |
| if ($default == $position) { |
| $new = 0; |
| } elsif ($default > $position) { |
| $new = $default - 1; |
| } |
| if ($default != $new) { |
| $self->set_default($new); |
| } |
| |
| print "Removed: $title\n"; |
| } |
| |
| |
| # Update kernel args |
| sub update { |
| my $self=shift; |
| my %params=@_; |
| |
| print ("Updating kernel.\n") if $self->debug()>1; |
| |
| if (defined $params{'option'} && !defined $params{'update-kernel'}) { |
| return $self->update_main_options(%params); |
| } elsif (!defined $params{'update-kernel'} || (!defined $params{'args'} && !defined $params{'remove-args'} && !defined $params{'option'})) { |
| warn "ERROR: kernel position or title (--update-kernel) and args (--args or --remove-args) required.\n"; |
| return undef; |
| } |
| |
| return undef unless $self->_check_config(); |
| |
| # my @config = @{$self->{config}}; |
| my @sections=$self->_info(); |
| |
| # if not a number, do title lookup |
| if (defined $params{'update-kernel'} and $params{'update-kernel'} !~ /^\d+$/) { |
| $params{'update-kernel'} = $self->_lookup($params{'update-kernel'}); |
| } |
| |
| my $kcount = $#sections-1; |
| if ($params{'update-kernel'} !~ /^\d+$/ || $params{'update-kernel'} < 0 || $params{'update-kernel'} > $kcount) { |
| warn "ERROR: Enter a default between 0 and $kcount.\n"; |
| return undef; |
| } |
| |
| # Convert to a title to find the relevant section. |
| my $title = $sections[$params{'update-kernel'} + 1]{'title'}; |
| |
| my $seen = ''; |
| my $config_line = -1; |
| my $line = ''; |
| foreach $line (@{$self->{config}}) { |
| $config_line = $config_line + 1; |
| if ($line =~ /^\s*\[(\S+)]/i) { |
| $seen = $1; |
| } |
| if ($title eq $seen) { |
| if (defined $params{'args'} or defined $params{'remove-args'}){ |
| if ($line =~ /^\s*parameters="(.*[^"])"/i) { |
| my $oargs = $1; |
| my $args = $self->_build_args($oargs, $params{'remove-args'}, $params{'args'}); |
| if ($args eq $oargs) { |
| warn "WARNING: No change made to args.\n"; |
| return undef; |
| } |
| # Note that updating line updates the _real_ lines in @config. |
| $line = "\tparameters=\"$args\"\n"; |
| next; |
| } |
| } elsif (defined $params{'option'}){ |
| foreach my $val ( keys %params){ |
| if ($line =~ m/^\s*$val.*/i) { |
| splice @{$self->{config}},$config_line,1,"$val $params{$val}\n"; |
| delete $params{$val}; |
| $config_line += 1; |
| } |
| } |
| } |
| } |
| } |
| # Add any leftover parameters |
| delete $params{'update-kernel'}; |
| if (defined $params{'option'}){ |
| delete $params{'option'}; |
| $config_line -= 1; |
| foreach my $val ( keys %params){ |
| splice @{$self->{config}},$config_line,0,"\t$val $params{$val}\n"; |
| $config_line += 1; |
| } |
| } |
| } |
| |
| |
| # Run command to install bootloader |
| sub install { |
| my $self=shift; |
| my $device; |
| |
| my @sections=$self->_info(); |
| |
| warn "ZIPL: needs to run zipl -m $sections[0]{'menu'}\n"; |
| system("/sbin/zipl -m $sections[0]{'menu'}"); |
| if ($? != 0) { |
| warn ("ERROR: Failed to run grub-install.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| |
| sub update_main_options{ |
| # XXX: the main options are probabally those on the menu object. |
| die "ERROR: unable to update main options\n"; |
| } |
| |
| |
| sub boot_once { |
| warn "ZIPL does not support boot-once\n"; |
| return undef; |
| } |
| |
| 1; |
| |
| =head1 AUTHOR |
| |
| Open Source Development Labs, Engineering Department <eng@osdl.org> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or modify it |
| under the same terms as Perl itself. |
| |
| =head1 SEE ALSO |
| |
| L<Linux::Bootloader> |
| |
| =cut |
| |
| #!/usr/bin/perl -I ../lib |
| |
| use lib '../lib'; |
| use Getopt::Long; |
| use Pod::Usage; |
| |
| |
| my %params; |
| |
| GetOptions( |
| \%params, |
| "bootloader-probe", # Prints the bootloader in use on the system |
| "arch-probe:s", # Prints the arch of the system |
| "bootloader=s", |
| "config_file=s", |
| "add-kernel=s", |
| "remove-kernel=s", |
| "update-kernel=s", |
| "title=s", |
| "args=s", |
| "remove-args=s", |
| "initrd=s", |
| "root=s", |
| "savedefault=s", |
| "position=s", |
| "info=s", |
| "debug=i", |
| "set-default=s", |
| "make-default", |
| "force", |
| "boot-once", |
| "install", |
| "module=s@", |
| "default", |
| "help", |
| "man", |
| "version|V", |
| "xen", |
| ) or pod2usage(-verbose => 1, -exitstatus => 0); |
| |
| pod2usage(-verbose => 2, -exitstatus => 0) if ($params{man}); |
| pod2usage(-verbose => 1, -exitstatus => 0) if ($params{help}); |
| pod2usage(-verbose => 0, -exitstatus => 0) if ! %params; |
| |
| if ($params{version}) { |
| print "$0 version 1.1\n"; |
| } |
| |
| ### Bootloader / Arch Detection ### |
| |
| my $detected_bootloader; |
| my $detected_architecture; |
| |
| if (defined $params{'bootloader-probe'}) { |
| our $opt_bootloader = 0; |
| $detected_bootloader = Linux::Bootloader::Detect::detect_bootloader() |
| || warn "Could not detect bootloader\n"; |
| print "$detected_bootloader\n"; |
| exit 0; |
| } elsif (defined $params{'arch-probe'}) { |
| our $opt_arch = 0; |
| $detected_architecture = Linux::Bootloader::Detect::detect_architecture( $params{'arch-probe'} ) |
| || warn "Could not detect architecture\n"; |
| print "$detected_architecture\n"; |
| exit 0; |
| } elsif (defined $params{bootloader}) { |
| $detected_bootloader = $params{bootloader}; |
| } else { |
| #$detected_bootloader = 'grub'; |
| $detected_bootloader = Linux::Bootloader::Detect::detect_bootloader() |
| || warn "Could not detect bootloader\n"; |
| } |
| |
| |
| ### Load Module ### |
| |
| my $bootloader; |
| if ($detected_bootloader =~ m/^(grub|elilo|lilo|yaboot|zipl)$/) { |
| my $class = "Linux::Bootloader::" . "\u$detected_bootloader"; |
| eval "require $class"; |
| $bootloader = eval "new $class(\$params{config_file});"; |
| |
| } else { |
| die "ERROR: Bootloader $detected_bootloader not recognized!\n"; |
| } |
| |
| |
| ### Check Config ### |
| |
| if (! -r $bootloader->{config_file}) { die "Can't read config file.\n"; } |
| |
| if (defined $params{'debug'}) { |
| $bootloader->debug($params{'debug'}); |
| } |
| |
| if (defined $params{'install'}) { |
| $bootloader->read(); |
| $bootloader->install() unless $detected_bootloader eq 'grub' |
| or $detected_bootloader eq 'pxe' ; |
| } elsif (defined $params{'add-kernel'}) { |
| $bootloader->read(); |
| $bootloader->add(%params); |
| $bootloader->write(); |
| $bootloader->install() unless $detected_bootloader eq 'grub'; |
| |
| } elsif (defined $params{'remove-kernel'}) { |
| $bootloader->read(); |
| $bootloader->remove($params{'remove-kernel'}); |
| $bootloader->write(); |
| $bootloader->install() unless $detected_bootloader eq 'grub'; |
| |
| } elsif (defined $params{'update-kernel'}) { |
| $bootloader->read(); |
| $bootloader->update(%params); |
| $bootloader->write(); |
| $bootloader->install() unless $detected_bootloader eq 'grub'; |
| |
| } elsif (defined $params{info}) { |
| $bootloader->read(); |
| $bootloader->print_info($params{info}); |
| |
| } elsif (defined $params{'set-default'}) { |
| $bootloader->read(); |
| $bootloader->set_default($params{'set-default'}); |
| $bootloader->write(); |
| $bootloader->install() unless $detected_bootloader eq 'grub'; |
| |
| } elsif (defined $params{'default'}) { |
| $bootloader->read(); |
| print $bootloader->get_default() . "\n"; |
| |
| } elsif (defined $params{'boot-once'} && defined $params{'title'}) { |
| if ($detected_bootloader =~ /^lilo|^elilo|^grub/) { |
| $bootloader->boot_once($params{title}); |
| } else { |
| warn "WARNING: $detected_bootloader does not have boot-once support.\n"; |
| warn "Setting as default instead.\n"; |
| $bootloader->read(); |
| $bootloader->set_default($params{'title'}); |
| $bootloader->write(); |
| } |
| } |
| |
| |
| __END__ |
| |
| |
| =head1 NAME |
| |
| boottool - tool for modifying bootloader configuration |
| |
| =head1 SYNOPSIS |
| |
| boottool [--bootloader-probe] [--arch-probe] |
| [--add-kernel=<kernel_path>] [--title=<kernel_title>] [--position=<#|start|end>] |
| [--root=<root_path>] [--args=<kernel_args>] [--initrd=<initrd_path>] |
| [--make-default] [--force] [--boot-once] [--install] |
| [--bootloader=<grub|lilo|elilo|yaboot|zipl>] [--config-file=</path/to/config>] |
| [--remove-kernel=<#|title|start|end>] [--module=<module>] |
| [--update-kernel=<#|title>] [--remove-args=<args>] |
| [--info=<all|default|#>] [--default] |
| [--help] [--debug=<0..5>] [--set-default=<#>] |
| |
| =head1 DESCRIPTION |
| |
| Boottool allows scripted modification of bootloader configuration files. |
| Grub, Lilo, Elilo, and Yaboot are currently supported. |
| When adding a kernel, any options not specified are copied from default. |
| |
| =head1 OPTIONS |
| |
| =head2 GENERAL OPTIONS |
| |
| These can be used with any of the commands to override defaults or |
| autodetection. They are not typically needed. |
| |
| =over 8 |
| |
| =item B<--bootloader>=I<string> |
| |
| Manually specify the bootloader to use. By default, boottool will |
| automatically try to detect the bootloader being used. |
| |
| =item B<--config_file>=I<string> |
| |
| Specifies the path and name of the bootloader config file, overriding |
| autodetection of this file. |
| |
| =back |
| |
| =head2 INFORMATIONAL OPERATIONS |
| |
| These operations return information about the system, without making |
| alterations to any files. |
| |
| =over 8 |
| |
| =item B<--bootloader-probe> |
| |
| Prints the bootloader in use on the system and exits. |
| |
| =item B<--arch-probe> |
| |
| Prints the arch of the system and exits. |
| |
| =item B<--info>=I<string> |
| |
| Display information about the bootloader entry at the given position number. |
| Also accepts 'all' or 'default'. |
| |
| =item B<--default> |
| |
| Prints the current default kernel for the bootloader. |
| |
| =back |
| |
| =head2 KERNEL OPERATIONS |
| |
| These operations result in modifications to system configuration files. |
| Only one of these operations may be called. See KERNEL MODIFICATION |
| PARAMETERS (below) for specifying what the operations should do. |
| |
| =over 8 |
| |
| =item B<--add-kernel>=I<string> |
| |
| Adds a new kernel with the given path. |
| |
| =item B<--update-kernel>=I<string> |
| |
| Updates an existing kernel with the given position number or title. |
| Used with --args or --remove-args. |
| |
| =item B<--module>=I<string> |
| |
| This option adds modules to the new kernel. It only works with Grub Bootloader. |
| For more module options just add another --module parameter |
| |
| =item B<--remove-kernel>=I<string> |
| |
| Removes the bootloader entry with the given position or title. |
| Also accepts 'start' or 'end'. |
| |
| =item B<--set-default>=I<integer> |
| |
| Updates the bootloader to set the default boot entry to given given |
| position or title. |
| |
| =item B<--boot-once> |
| |
| Causes the bootloader to boot the kernel specified by --title just one |
| time, then fall back to the default. This option doesn't work |
| identically on all architectures. |
| |
| =back |
| |
| =head2 KERNEL MODIFICATION PARAMETERS |
| |
| These parameters can be used with the kernel operations listed above, to |
| specify how the operations should work. |
| |
| =over 8 |
| |
| =item B<--title>=I<string> |
| |
| The title or label to use for the bootloader entry. |
| |
| =item B<--args>=I<string> |
| |
| Arguments to be passed to the kernel at boot. |
| |
| =item B<--remove-args>=I<string> |
| |
| Arguments to be removed from an existing entry. |
| Used with --update-kernel. |
| |
| =item B<--initrd>=I<string> |
| |
| The initrd image path to use in the bootloader entry. |
| |
| =item B<--root>=I<string> |
| |
| The device where the root partition is located. |
| |
| =item B<--savedefault>=I<string> |
| |
| The number to use in the savedefault section |
| |
| =item B<--position>=I<string> |
| |
| Insert bootloader entry at the given position number, counting from 0. |
| Also accepts 'start' or 'end'. This is only useful when using the |
| --add-kernel operation. |
| |
| =item B<--make-default> |
| |
| Specifies that the bootloader entry being added should be set to the |
| default. |
| |
| =item B<--install> |
| |
| Causes bootloader to update and re-install the bootloader file. |
| |
| =back |
| |
| |
| =head2 OTHER OPTIONS |
| |
| =over 8 |
| |
| =item B<-V, --version> |
| |
| Prints the version and exits. |
| |
| =item B<-h, --help> |
| |
| Prints a brief help message with option summary. |
| |
| =item B<--man> |
| |
| Prints a manual page (detailed help). Same as `perdoc tgen` |
| |
| =item B<-D, --debug N> |
| |
| Prints debug messages. This expects a numerical argument corresponding |
| to the debug message verbosity. |
| |
| =back |
| |
| =head1 PREREQUISITES |
| |
| C<Linux::Bootloader> |
| |
| C<Getopt::Long> |
| |
| C<Pod::Usage> |
| |
| =head1 COREQUISITES |
| |
| boottool works with any bootloader supported by Linux::Bootloader, |
| including the following: |
| |
| C<Lilo> |
| |
| C<Grub> |
| |
| C<Yaboot> |
| |
| C<Elilo> |
| |
| Obviously, at least one bootloader must be installed for this to be of |
| any use. ;-) |
| |
| =head1 BUGS |
| |
| Send bug reports to L<http://sourceforge.net/projects/crucible/> |
| |
| =head1 VERSION |
| |
| 1.0 |
| |
| =head1 SEE ALSO |
| |
| L<crucible>, L<WWW::PkgFind>, L<Test::Parser>, L<Linux::Distribution> |
| |
| =head1 AUTHOR |
| |
| Jason N. |
| |
| L<http://www.osdl.org/|http://www.osdl.org/> |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2006 Open Source Development Labs |
| All Rights Reserved. |
| |
| This script is free software; you can redistribute it and/or |
| modify it under the same terms as Perl itself. |
| |
| =head1 REVISION |
| |
| Revision: $Revision: 1.10 $ |
| |
| =cut |