| #!/usr/bin/perl -w |
| # |
| # Monolithic script to modify bootloader configuration. |
| # Supports GRUB, LILO, ELILO, Yaboot. |
| # Functions taken from Linux::Bootloader module. |
| |
| use strict; |
| use Getopt::Long; |
| |
| my @bootconfig = []; |
| my $debug = 0; |
| my $bootloader; |
| my %params; |
| |
| GetOptions( |
| \%params, |
| "bootloader-probe", # Prints the bootloader in use on the system |
| "arch-probe:s", # Prints the arch of the system |
| "bootloader|b=s", |
| "config_file=s", |
| "add-kernel|a=s", |
| "remove-kernel|r=s", |
| "update-kernel|u=s", |
| "title=s", |
| "args=s", |
| "remove-args=s", |
| "initrd=s", |
| "root=s", |
| "savedefault=s", |
| "position=s", |
| "info|i=s", |
| "debug|d=i", |
| "set-default=s", |
| "make-default", |
| "force", |
| "boot-once", |
| "install", |
| "default", |
| "help", |
| "man", |
| "xen", |
| "xenhyper|xh=s", |
| "xenhyper-args|xha=s", |
| "update-xenhyper=s", |
| ); |
| |
| &usage if ( !%params || defined $params{help} ); |
| |
| |
| ### Detection Functions ### |
| |
| 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; |
| } |
| |
| sub detect_bootloader { |
| return detect_bootloader_from_conf(@_) |
| || detect_bootloader_from_mbr(@_); |
| } |
| |
| 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' |
| ); |
| |
| 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); |
| } |
| else { |
| return undef; |
| } |
| } |
| |
| 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 (@filelist) { |
| if ( -b "/dev/$_" ) { |
| my $strings = `dd if=/dev/$_ bs=512 count=1 2>/dev/null | 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; |
| } |
| else { |
| |
| # Either none or too many to choose from |
| return undef; |
| } |
| } |
| |
| ### GRUB functions ### |
| |
| # Parse config into array of hashes |
| |
| sub _info_grub { |
| |
| return undef unless &_check_config(); |
| |
| my @config = @bootconfig; |
| @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_grub { |
| my $newdefault = shift; |
| |
| return undef unless defined $newdefault; |
| return undef unless &_check_config(); |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| # if not a number, do title lookup |
| if ( $newdefault !~ /^\d+$/ ) { |
| $newdefault = &_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; |
| } |
| |
| foreach my $index ( 0 .. $#config ) { |
| if ( $config[$index] =~ /(^\s*default\s*\=*\s*)\d+/i ) { |
| $config[$index] = "# set by $0\n$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); |
| |
| $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; |
| } |
| } |
| @bootconfig = @config; |
| } |
| |
| # Add new kernel to config |
| |
| sub add_grub { |
| my %param = @_; |
| |
| print("Adding kernel.\n") if &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 &_check_config(); |
| |
| my @sections = &_info(); |
| |
| # check if title already exists |
| if ( defined &_lookup( $param{title} ) ) { |
| warn("WARNING: Title already exists.\n"); |
| if ( defined $param{force} ) { |
| &remove( $param{title} ); |
| } |
| else { |
| return undef; |
| } |
| } |
| |
| my @config = @bootconfig; |
| @sections = &_info(); |
| |
| # Use default kernel to fill in missing info |
| my $default = &get_default(); |
| $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 |
| if ( $sections[$default]{'kernel'} !~ /^\/boot/ ) { |
| $param{'add-kernel'} =~ s/^\/boot//; |
| $param{'initrd'} =~ s/^\/boot// 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} ) { |
| if ( defined $param{'xenhyper'} ) { |
| $line = "\tkernel $param{'xenhyper'}"; |
| $line .= " $param{'xenhyper-args'}" if defined $param{'xenhyper-args'}; |
| push ( @newkernel, "$line\n" ); |
| } else { |
| $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" ); |
| } |
| $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}; |
| 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, $_ ); |
| } |
| } |
| |
| @bootconfig = @newconfig; |
| |
| if ( defined $param{'make-default'} || defined $param{'boot-once'} ) { |
| &set_default( $param{position} ); |
| } |
| print "Added: $param{'title'}.\n"; |
| } |
| |
| # Update kernel args |
| |
| sub update_grub { |
| my %params = @_; |
| |
| print("Updating kernel.\n") if &debug() > 1; |
| |
| if ( !defined $params{'update-kernel'} && !defined $params{'xenhyper-args'} |
| || ( !defined $params{'args'} && !defined $params{'remove-args'} |
| && !defined $params{'xenhyper-args'} ) ) |
| { |
| warn |
| "ERROR: kernel position or title (--update-kernel) and args (--args or --remove-args) required.\n"; |
| return undef; |
| } |
| |
| return undef unless &_check_config(); |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| # if not a number, do title lookup |
| if ( $params{'update-kernel'} !~ /^\d+$/ ) { |
| $params{'update-kernel'} = &_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+)(.*)'; |
| if ( !defined $params{'update-xenhyper'} && defined $params{'xen'} ) |
| { |
| $kregex = '(^\s*module\s+\S+vmlinuz\S+)(.*)' if defined $params{'xen'}; |
| } |
| |
| my $index = -1; |
| foreach (@config) { |
| if ( $_ =~ /^\s*title/i ) { |
| $index++; |
| } |
| if ( $index == $params{'update-kernel'} ) { |
| if ( $_ =~ /$kregex/i ) { |
| my $kernel = $1; |
| my $args = $2; |
| $args =~ s/\s+$params{'remove-args'}(\=\S+|\s+|$)/ /ig |
| if defined $params{'remove-args'}; |
| if ( defined $params{'args'} || defined $params{'xenhyper-args'} ) { |
| $params{'args'} = $params{'xenhyper-args'} if defined $params{'xenhyper-args'}; |
| my $base_arg = $params{'args'}; |
| $base_arg =~ s/\=.*//; |
| $args =~ s/\s+$base_arg(\=\S+|\s+|$)/ /ig; |
| $args = $args . " " . $params{'args'}; |
| } |
| if ( $_ eq $kernel . $args . "\n" ) { |
| warn "WARNING: No change made to args.\n"; |
| return undef; |
| } |
| else { |
| $_ = $kernel . $args . "\n"; |
| } |
| next; |
| } |
| } |
| } |
| @bootconfig = @config; |
| } |
| |
| # Run command to install bootloader |
| |
| sub install_grub { |
| 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; |
| } |
| |
| ### LILO functions ### |
| |
| # Run command to install bootloader |
| |
| sub install_lilo { |
| |
| 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_lilo { |
| my $label = shift; |
| |
| return undef unless defined $label; |
| |
| if ( system( "lilo", "-R", "$label" ) ) { |
| warn("ERROR: Failed to set boot-once.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| ### ELILO functions ### |
| |
| # Run command to install bootloader |
| |
| sub install_elilo { |
| |
| system("/usr/sbin/elilo"); |
| if ( $? != 0 ) { |
| warn("ERROR: Failed to run elilo.\n") && return undef; |
| } |
| return 1; |
| } |
| |
| # Set kernel to be booted once |
| |
| sub boot_once_elilo { |
| my $label = shift; |
| |
| return undef unless defined $label; |
| |
| &read( '/etc/elilo.conf' ); |
| my @config = @bootconfig; |
| |
| 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 = &_info(); |
| my $position = &_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( "eliloalt", "-d" ); |
| if ( system( "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; |
| } |
| |
| |
| ### YABOOT functions ### |
| |
| # Run command to install bootloader |
| |
| sub install_yaboot { |
| |
| #system("/usr/sbin/ybin"); |
| #if ( $? != 0 ) { |
| # warn("ERROR: Failed to run ybin.\n") && return undef; |
| #} |
| |
| print("Not installing bootloader.\n"); |
| print("Depending on your arch you may need to run ybin.\n"); |
| return 1; |
| } |
| |
| ### Generic Functions ### |
| |
| # Read config file into array |
| |
| sub read { |
| my $config_file = shift; |
| print("Reading $config_file.\n") if debug() > 1; |
| |
| open( CONFIG, "$config_file" ) |
| || warn("ERROR: Can't open $config_file.\n") && return undef; |
| @bootconfig = <CONFIG>; |
| close(CONFIG); |
| |
| print("Current config:\n @bootconfig") if &debug() > 4; |
| print("Closed $config_file.\n") if &debug() > 2; |
| return 1; |
| } |
| |
| # Write new config |
| |
| sub write { |
| my $config_file = shift; |
| my @config = @bootconfig; |
| |
| return undef unless &_check_config(); |
| |
| print("Writing $config_file.\n") if &debug() > 1; |
| print join( "", @config ) if &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 &debug() > 2; |
| warn "WARNING: You do not have write access to $config_file.\n"; |
| |
| return 1; |
| } |
| } |
| |
| # Parse config into array of hashes |
| |
| sub _info { |
| |
| return &_info_grub() if ( $bootloader eq 'grub' ); |
| |
| return undef unless &_check_config(); |
| my @config = @bootconfig; |
| |
| # 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 |
| } |
| 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; |
| } |
| } |
| } |
| |
| # 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 { |
| |
| print("Getting default.\n") if &debug() > 1; |
| return undef unless &_check_config(); |
| |
| my @sections = &_info(); |
| my $default = $sections[0]{'default'}; |
| |
| $default = 0 + $default; |
| return ($default); |
| } |
| |
| # Set new default kernel |
| |
| sub set_default { |
| my $newdefault = shift; |
| |
| return &set_default_grub($newdefault) if ( $bootloader eq 'grub' ); |
| |
| print("Setting default.\n") if &debug() > 1; |
| |
| return undef unless defined $newdefault; |
| return undef unless &_check_config(); |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| # if not a number, do title lookup |
| if ( $newdefault !~ /^\d+$/ ) { |
| $newdefault = &_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; |
| } |
| } |
| @bootconfig = @config; |
| } |
| |
| # Add new kernel to config |
| |
| sub add { |
| my %param = @_; |
| |
| return &add_grub(%param) if ( $bootloader eq 'grub' ); |
| |
| print("Adding kernel.\n") if &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 &_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 = &_info(); |
| |
| # check if title already exists |
| if ( defined &_lookup( $param{title} ) ) { |
| warn("WARNING: Title already exists.\n"); |
| if ( defined $param{force} ) { |
| &remove( $param{title} ); |
| } |
| else { |
| return undef; |
| } |
| } |
| |
| my @config = @bootconfig; |
| @sections = &_info(); |
| |
| # Use default kernel to fill in missing info |
| my $default = &get_default(); |
| $default++; |
| |
| foreach my $p ( 'args', 'root' ) { |
| if ( !defined $param{$p} ) { |
| $param{$p} = $sections[$default]{$p}; |
| } |
| } |
| |
| 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, $_ ); |
| } |
| } |
| |
| @bootconfig = @newconfig; |
| |
| if ( defined $param{'make-default'} ) { |
| &set_default( $param{position} ); |
| } |
| } |
| |
| # Update kernel args |
| |
| sub update { |
| my %params = @_; |
| |
| return &update_grub(%params) if ( $bootloader eq 'grub' ); |
| |
| print("Updating kernel.\n") if &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 &_check_config(); |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| # if not a number, do title lookup |
| if ( $params{'update-kernel'} !~ /^\d+$/ ) { |
| $params{'update-kernel'} = &_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 =~ s/\s*$params{'remove-args'}\=*\S*//ig |
| if defined $params{'remove-args'}; |
| $args = $args . " " . $params{'args'} |
| if defined $params{'args'}; |
| if ( $_ eq "$append\"$args\"\n" ) { |
| warn "WARNING: No change made to args.\n"; |
| return undef; |
| } |
| else { |
| $_ = "$append\"$args\"\n"; |
| } |
| next; |
| } |
| } |
| } |
| @bootconfig = @config; |
| } |
| |
| # Remove kernel from config |
| |
| sub remove { |
| my $position = shift; |
| my @newconfig; |
| |
| return undef unless defined $position; |
| return undef unless &_check_config(); |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| if ( $position =~ /^end$/i ) { |
| $position = $#sections - 1; |
| } |
| elsif ( $position =~ /^start$/i ) { |
| $position = 0; |
| } |
| |
| print("Removing kernel $position.\n") if &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 &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, $_ ); |
| } |
| } |
| @bootconfig = @newconfig; |
| |
| # if we removed the default, set new default to first |
| &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 $info = shift; |
| |
| return undef unless defined $info; |
| return undef unless &_check_config(); |
| |
| print("Printing config info.\n") if &debug() > 1; |
| |
| my @config = @bootconfig; |
| my @sections = &_info(); |
| |
| my ( $start, $end ); |
| if ( $info =~ /default/i ) { |
| $start = $end = &get_default(); |
| } |
| elsif ( $info =~ /all/i ) { |
| $start = 0; |
| $end = $#sections - 1; |
| } |
| elsif ( $info =~ /^\d+/ ) { |
| $start = $end = $info; |
| } |
| else { |
| warn "ERROR: input should be: #, default, or all.\n"; |
| return undef; |
| } |
| |
| 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"; |
| } |
| } |
| } |
| |
| # Attempt to install bootloader |
| |
| sub install { |
| return &install_lilo() if ( $bootloader eq 'lilo' ); |
| return &install_grub() if ( $bootloader eq 'grub' ); |
| return &install_elilo() if ( $bootloader eq 'elilo' ); |
| return &install_yaboot() if ( $bootloader eq 'yaboot' ); |
| } |
| |
| # Set/get debug level |
| |
| sub debug { |
| if (@_) { |
| $debug = shift; |
| } |
| return $debug; |
| } |
| |
| # Basic check for valid config |
| |
| sub _check_config { |
| |
| print("Verifying config.\n") if &debug() > 3; |
| |
| if ( $#bootconfig < 5 ) { |
| warn "ERROR: you must read a valid config file first.\n"; |
| return undef; |
| } |
| return 1; |
| } |
| |
| # lookup position using title |
| |
| sub _lookup { |
| my $title = shift; |
| |
| my @sections = &_info(); |
| |
| for my $index ( 1 .. $#sections ) { |
| if ( ( defined $sections[$index]{title} ) |
| && ( $title eq $sections[$index]{title} ) ) |
| { |
| return $index - 1; |
| } |
| } |
| return undef; |
| } |
| |
| ### Bootloader / Arch Detection ### |
| |
| my $detected_bootloader; |
| my $detected_architecture; |
| |
| if ( defined $params{'bootloader-probe'} ) { |
| $detected_bootloader = detect_bootloader() |
| || warn "Could not detect bootloader\n"; |
| print "$detected_bootloader\n"; |
| exit 0; |
| } |
| elsif ( defined $params{'arch-probe'} ) { |
| $detected_architecture = 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 = detect_bootloader() |
| || die "Could not detect bootloader\n"; |
| } |
| |
| ### Do Config ### |
| |
| $bootloader = $detected_bootloader; |
| |
| my %cfg_files = ( |
| grub => '/boot/grub/menu.lst', |
| lilo => '/etc/lilo.conf', |
| elilo => '/etc/elilo.conf', |
| yaboot => '/etc/yaboot.conf' |
| ); |
| |
| $params{config_file} = $cfg_files{$bootloader} |
| unless defined $params{config_file}; |
| die("Can't read config file.\n") unless ( -r $params{config_file} ); |
| &debug( $params{'debug'} ) if ( defined $params{'debug'} ); |
| |
| if ( defined $params{'add-kernel'} || defined $params{'xenhyper'} ) { |
| &read( $params{config_file} ); |
| &add(%params); |
| &write( $params{config_file} ); |
| &install() unless $detected_bootloader eq 'grub'; |
| |
| } |
| elsif ( defined $params{'remove-kernel'} ) { |
| &read( $params{config_file} ); |
| &remove( $params{'remove-kernel'} ); |
| &write( $params{config_file} ); |
| &install() unless $detected_bootloader eq 'grub'; |
| |
| } |
| elsif ( defined $params{'update-kernel'} || defined $params{'update-xenhyper'} ) { |
| $params{'update-kernel'} = $params{'update-xenhyper'} if defined $params{'update-xenhyper'}; |
| &read( $params{config_file} ); |
| &update(%params); |
| &write( $params{config_file} ); |
| &install() unless $detected_bootloader eq 'grub'; |
| |
| } |
| elsif ( defined $params{info} ) { |
| &read( $params{config_file} ); |
| &print_info( $params{info} ); |
| |
| } |
| elsif ( defined $params{'set-default'} ) { |
| &read( $params{config_file} ); |
| &set_default( $params{'set-default'} ); |
| &write( $params{config_file} ); |
| &install() unless $detected_bootloader eq 'grub'; |
| |
| } |
| elsif ( defined $params{'default'} ) { |
| &read( $params{config_file} ); |
| print get_default() . "\n"; |
| |
| } |
| elsif ( defined $params{'boot-once'} && defined $params{'title'} ) { |
| if ( $detected_bootloader eq 'lilo' ) { |
| &boot_once_lilo( $params{title} ); |
| } elsif ( $detected_bootloader eq 'elilo' ) { |
| &boot_once_elilo( $params{title} ); |
| } else { |
| print "$detected_bootloader does not have boot-once support.\n"; |
| print "Setting as default instead.\n"; |
| &read( $params{config_file} ); |
| &set_default( $params{'title'} ); |
| &write( $params{config_file} ); |
| } |
| } |
| |
| sub usage { |
| print "Usage: |
| 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>] [--config-file=<config_path>] |
| [--remove-kernel=<#|title|start|end>] |
| [--update-kernel=<#|title>] [--remove-args=<args>] |
| [--info=<all|default|#>] [--default] [--set-default=<#>] |
| [--xen] [--xenhyper=<kernel_path>] [--xenhyper-args=<kernel_args>] |
| [--help] [--debug=<0..5>] |
| |
| Examples: |
| boottool --info all # Print config info |
| boottool -a /boot/vmlinuz -t 'test' -p end # Add a new kernel |
| boottool --remove-kernel 3 # Remove a kernel |
| boottool -u title1 -remove-args 'ro' # Remove args from a kernel |
| boottool -u title1 -args 'arg1=test' --xen # Add args to a Xen kernel |
| boottool -xh /boot/xen.gz -a /boot/vmlinuz -t test --xen # Add Xen kernel |
| boottool --set-default 1 # Set new default\n"; |
| exit 1; |
| } |