File elilo.pl of Package elilo

#!/usr/bin/perl -w
# $Id: elilo.pl,v 0.12 2005/12/09 11:46:06 rw Exp $
use strict;

my $C = $0; $C =~ s%^.*/%%;

my $dbg = (exists( $ENV{"ELILO_DEBUG"})) ? $ENV{"ELILO_DEBUG"} : "";
my $Edition = q(@EDITION@);
my $MPold  = "$dbg/boot";
my $MPnew  = "$dbg/boot/efi";
my $Dlib  = "$dbg/usr/lib/elilo";
my $Fconf = "elilo.conf";
my $Sconf = "$dbg/etc/" . $Fconf;

my $Reserved = qr(^(efi-mountpoint|vendor-directory|elilo-origin))o;
my %Sconf = ();

my $test = 0;
my $verbose = 0;
my $warn = 0;
my $optional = 1;
my $MP = "";		# Mount-Point for EFI/FAT partition
my $VD = "SuSE";	# vendor-specific directory in $MP/efi
my $D = "";		# will be $MP.$VD
my %Labels = ();

my $Disclaimer = <<EoD;
# This file has been transformed by /sbin/elilo.
# Please do NOT edit here -- edit /etc/elilo.conf instead!
# Otherwise your changes will be lost e.g. during kernel-update.
#
EoD

$| = 1;
$[ = 0;

sub Version() {
  my $v = q($Revision: 0.12 $ );
  $v =~ s/^\$ Rev.*:\ ([0-9.]+)\ \$\ /$1/x;
  $v .= " (part of elilo-$Edition)" if ( $Edition ne "\@EDITION\@" );
  print "$C version $v\n";
  exit( 0 );
}
sub Info ($$) {
  print STDERR $_[1] if ( $verbose >= $_[0] );
}
sub Warn ($) {
  print STDERR "$C: Warning: $_[0]";
  $warn ++;
}
sub Panic ($$) {
  print STDERR "$C: $_[1]";
  exit( $_[0]) if ( $_[0] );
  $warn ++;
  print STDERR "...trying to proceed anyway!\n";
}
sub Parse ($) {
  my( $f) = @_;
  my %r = ();

  open( IN, "< $f") || return( %r );
  # fixme: add failure-detection
  while ( <IN> ) {
    chomp;
    next unless ( m/$Reserved\s*\=\s*(.+)?\s*$/xo );
    $r{$1} = $2;
  }
  close( IN);
  return( %r );
}
sub Transfer ($$) {
  my( $in, $dir) = @_;
  my( $out, $tmp, $c, @f, %f, $opt);
  my $default_label = "";
  my $default_loc;
  my @Out = ();
  $out = "$dir/$Fconf";
  $tmp = "$out.tmp";
  $opt = $optional;

  open( IN, "< $in") || Panic(1, "$in: failed to open: $!\n");
  Info( 1, "## filter '$in' to");
  if ( ! $test ) {
    Info( 1, " '$tmp'\n");
    open(OUT, "> $tmp") || Panic( 1, "$tmp: failed to create: $!\n");
    push @Out, $Disclaimer;
  } elsif ( $verbose >= 2 ) {
    Info( 1, " STDOUT\n");
    open( OUT, ">&STDOUT");
    push @Out, $Disclaimer unless ( $verbose < 3 );
  } else {
    Info( 1, " /dev/null\n");
    open( OUT, ">/dev/null");
  }
  while ( <IN> ) {
    next if ( m/$Reserved/xo );
    if ( m%^\s*(?:image|initrd)\s*=\s*% ) {
      chomp;
      s%^(\s*(?:image|initrd)\s*=\s*)(/\S+/)?([^/\s]+)\s*$%$1$3%;
      my( $t, $p, $f) = ($1, $2, $3);
      $_ .= "\n";
      if ( ! defined( $p) ) {
	$p = "/boot/";
      }
      if ( ! defined( $f) ) {
	Warn( "$in: $.: invalid file-specification\n" .
	      ">> $_");
	$c++;
      } elsif ( exists( $f{$f}) ) {
	Info( 3, "$in: $.: file duplication skipped (previous: $f{$f})\n" .
	      ">> $_");
      } elsif ( $opt && ! -r "$dbg$p$f" ) {
	Info( 0, "$C: $in: $.: missing optional '$p$f' skipped\n");
      } else {
	push @f, $dbg . "$p$f";
	$f{$f} = $.;
      }
    } elsif (m%^(\s*label\s*=\s*)(\S+)%) {
      my ($pre, $label) = ($1, $2);
      if (exists $Labels{$label}) {
	my $t = 1;
	my $l = $label;
	while ( exists $Labels{$l} ) {
	  $l = sprintf "%s.%d", $label, $t++;
	}
	Warn( "duplicate label '$label', replaced with '$l'\n");
	$label = $l;
	$_ = $pre . $l . "\n";
      }
      $Labels{$label} = 1;
    } elsif (m%^\s*default\s*=\s*(\S+)%) {
      $default_label = $1;
      $default_loc = $#Out + 1;
    }
    push @Out, $_;
  }
  if ($default_label ne "" && !exists $Labels{$default_label}) {
    $Out[$default_loc] = "#" . $Out[$default_loc];
    Warn( "undefined default label '$default_label' discarded\n");
  }

  print OUT @Out;
  close( OUT);
  close( IN);
  Info( 2, "## end of $in\n") unless $test;
  foreach ( @f ) {
    if ( ! -r $_ ) {
      Warn( "$_: not found\n");
      $c++;
    }
  }
  if ( $c ) {
    Info( 2, "> unlink( $tmp)\n");
    unlink( $tmp);
    Panic( 2, "$in: broken references\n");
  }
  if ( ! $test ) {
    Info( 1, "> unlink( $out)\n");
    unlink( $out);
    Info( 1, "> rename( $tmp, $out)\n");
    rename( $tmp, $out);
    # fixme: add failure-detection
  }
  return( @f );
}

sub System($@) {
  my( $fatal, @C) = @_;
  my $cmd = $C[0];
  my $rc = ($fatal) ? 1 : 0;

  foreach my $c ( @C[1..$#C] ) {
    if ( $c =~ /\s/ ) {
      $cmd .= " '$c'";
    } else {
      $cmd .= " $c";
    }
  }
  Info( 1, "> $cmd\n");
  return 0 if ( $test );

  system @C;
  if ($? == -1) {
    Panic( $rc, "$C[0]: failed to execute: $!\n");
  } elsif ($? & 127) {
    Panic( $rc, sprintf( "$C[0]: died with signal %d, %s coredump\n",
           ($? & 127),  ($? & 128) ? 'with' : 'without'));
  } elsif ( $? >> 8 != 0 ) {
    Panic( $rc, "$C[0]: failed\n");
  }
}

sub Install($$$$) {
  my( $f, $o, $s, $d) = @_;
  my @C = ( "install", $o, $s, $d);

  System( $f, @C);
}

sub InstallFPSWA($) {
  my ( $d) = @_;
  $d .= "/efi/Intel Firmware";
  return 0 unless ( -r "$Dlib/fpswa.efi" );

  my $head = "## fpswa: Floating Point Software Assist\n";
  if ( -d $d && -r "$d/fpswa.efi" ) {
    # check, if we need to update and failing that do nothing?!
    my $c = "$Dlib/fpswa-cmp-version";
    if ( -x $c ) {
      my $chk = `$c "$d/fpswa.efi" "$Dlib/fpswa.efi"`;
      if ( $chk =~ /older/ ) {
	Info( 1, $head .
	      "##    Update '$d/fpswa.efi'.\n");
	Info( 2,
	      "##      $chk");
	Install( 0, "-p", "$Dlib/fpswa.efi", $d);
      } else {
	Info( 1, $head .
	      "##    Do NOT update '$d/fpswa.efi'.\n");
	Info( 2,
	      "##      $chk");
      }
    } else {
      use File::Compare;
      if ( compare( "$d/fpswa.efi", "$Dlib/fpswa.efi") == 0 ) {
	Info( 2, $head .
	      "##    Already installed.\n");
      } else {
	Info( 1, $head .
	      "##    Unable to compare versions.\n" .
	      "##      Installation skipped!\n");
      }
    }
  } else {
    Info( 1, $head . "##    Install 'fpswa.efi' to '$d'.\n");
    System( 0, "mkdir", $d) unless ( -d $d );
    Install( 0, "-p", "$Dlib/fpswa.efi", $d);
  }
}

sub isMP($) {
  my ( $d) = @_;
  Info( 3, "### isMP($d):\n");
  foreach my $f ( ("/proc/mounts", "/etc/mtab") ) {
    Info( 4, "#### looking in $f\n");
    open( IN, "< $f") || next;
    while ( <IN> ) {
      chomp;
      my @F = split;
      if ( $F[1] eq $d ) {
	Info( 3, "### found in '$f' line $. => true\n");
        close( IN);
        return( 1);
      }
    }
    close( IN);
    Info( 3, "### not found in $f => false\n");
    return( 0);
  }
  Info( 3, "### no info available => false\n");
  return( 0);
}

{
  use Getopt::Long;
  use Pod::Usage;
  $Getopt::Long::debug = 0;
  $Getopt::Long::ignorecase = 0;
  $Getopt::Long::bundling = 1;
  $Getopt::Long::passthrough = 0;
  my %Opt = ();

  pod2usage(2) unless ( GetOptions( \%Opt,
     'help|h', 'man|m', 'version|V', 'verbose|v+',
     'test|t', 'purge') && ! $Opt{'help'} );

  Version() if ( $Opt{'version'} );
  pod2usage(-exitstatus => 0, -verbose => 2) if ( $Opt{'man'} );
  pod2usage(1) if ( $Opt{'help'} );
  $test = 1 if ( $Opt{'test'} );
  $verbose += $Opt{'verbose'} if ( $Opt{'verbose'} );
}

# try to read variables $Sconf
%Sconf = Parse( $Sconf);

# check environment
if ( exists( $Sconf{"efi-mountpoint"}) ) {
  $MP = $dbg . $Sconf{"efi-mountpoint"};
  Panic( 1, "EFI partions specification in $Sconf invalid.\n")
     unless ( -d $MP );  # or is it "$MP/efi"?
} elsif ( -d $MPnew . "/efi/" . $VD || isMP($MPnew) ) {
  $MP = $MPnew;
} elsif ( -d $MPold . "/efi/" . $VD ) {
  $MP = $MPold;
} else {
  Info( 1, "## Neither new ($MPnew/efi/$VD) nor old ($MPold/efi/$VD)?\n");
  Panic( 2, "EFI partiton not found.\n");
}
Info( 2, "## Mount-point '$MP'...\n");
if ( exists( $Sconf{"vendor-directory"}) ) {
  $VD = $Sconf{"vendor-directory"};
  Info( 1, "## Don't forget: '$VD != SuSE'--NVRAM (efibootmgr) issue!\n") 
     unless ( $VD eq "SuSE" );
}
$D = $MP . "/efi/" . $VD;
Info( 1, "## Using '$D'...\n");
System( 1, "mkdir", "-p", $D) unless ( -d $D );

if ( -r $Sconf ) {
  # extract kernels and initrds and write fixed .conf
  my @F = Transfer( $Sconf, $D);
  # copy stuff 
  unshift @F, "$Dlib/elilo.efi";
  foreach ( @F ) {
    Install( 0, "-p", $_, $D);
  }
  # take care of FPSWA
  InstallFPSWA( $MP);
} elsif ( $MP eq $MPold && -r "$D/$Fconf" ) {
  # assume old setup with only '/vmlinuz' and '/initrd'
  Install( 1, "-p", "$Dlib/elilo.efi", $D);
  InstallFPSWA( $MP);
} elsif ( $MP eq $MPold ) {
  Panic( 2, "$D/$Fconf: not found\n");
} else {
  Panic( 2, "$Sconf: not found\n");
}

if ( $warn > 0 ) {
  Panic( 1, sprintf("%d warning%s encountered.\n", $warn, ($warn==1)?"":"s"));
}
exit( 0);

__END__

=head1 NAME

elilo - Installer for the EFI Linux Loader

=head1 SYNOPSIS

/sbin/elilo [options]

 Options:
   -t --test            test only
   -v --verbose         increase verbosity
   -h --help            brief help message
      --man             full documentation
   -V --version         display version

=head1 OPTIONS

=over 8

=item B<--test>

Test only. Do not really write anything, no new boot configuration nor
kernel/initrd images.
Use together with B<-v> to find out what B<elilo> is about to do.

=item B<--verbose>

Increase level of verbosity.

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--version>

Prints the version information and exits.

=back

=head1 DESCRIPTION

This program will perform all steps to transfer the
necessary parts to the appropriate locations...



=head1 LIMITATIONS

For now, I<all> image-entries are treated as "optional" in
order to more closely match the behavior of the real
loader (i.e. C<elilo.efi>), which silently ignores missing files
while reading the configuration.

This may be considered a bug by experienced B<LILO> users,
where only those specifically marked as such are treated that way.

It is planned to introduce keywords like C<mandatory> and C<optional>
in future releases though.

=head1 SEE ALSO

/usr/share/doc/packages/elilo

=cut
openSUSE Build Service is sponsored by