summaryrefslogblamecommitdiffstats
path: root/src/boot-env/OpenSLX/MakeInitRamFS/Engine/Base.pm
blob: 9fd2c8bc06730892c21e9302635f164f8e6d7669 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                         








                                                                               

                                                             
                                                                               
                                             


             
           
 
                   
                       

                    
                        
                              






                                                                                














                                

                                






                                                                            
                                    
                               

                                           
 








                                                                             
     



                                                                            



                                             



           

                       
 
                          
 
                                                                      
                                         
 
           

 

                   





                                                              



                   




                                           



                


                                         



                    




                                                      

 


                                                                                

                


















                                                                                

 
          
 



                                
 
           
 
    

               













                                                                          

                             
                       
 
                                                     
 
           

 

                      









                                                                                  




                                                                         




                                                                  










                                                                                 






                                                                        


                                                                         
                           
                                                                                      






                                                                     



                                                         






























                                                                         

                                                                         
                  

                                                                                          

                                                      
                                 

                    


                                                                  
                                                               


                                                                    



                                                                                                                          



                                                                      






                                                                                                                   


                                                                                         
                                                           
                        
                                                                      
         
                                       

                                                                                  


                                                                  

        

                                                                 



                                                                         


                                                       



                                                             

 

                            

                       
 



                                          

 

                        




                                                                  
                                                                         


                                                                   
                                                                    
                                                        
                                                                  










                                                                



                       















                                                                               
                                                               

                           



                                                                               

                                                           
 

                                                                    





                                                                                

                

                                                                           
     

           

 
                     
 
                     
 


                                                                    
 


                                                                       










                                                                               

           

 

                    
                     
 
                                          
                                                





                                                
 
           


  
# Copyright (c) 2006..2010 - OpenSLX GmbH
#
# This program is free software distributed under the GPL version 2.
# See http://openslx.org/COPYING
#
# If you have any feedback please consult http://openslx.org/feedback and
# send your suggestions, praise, or complaints to feedback@openslx.org
#
# General information about OpenSLX can be found at http://openslx.org/
# -----------------------------------------------------------------------------
# MakeInitialRamFS::Engine::Base.pm
#    - provides basic driver engine for MakeInitialRamFS API.
# -----------------------------------------------------------------------------
package OpenSLX::MakeInitRamFS::Engine::Base;

use strict;
use warnings;
use Switch;

use File::Basename;
use POSIX qw(strftime);

use OpenSLX::Basics;
use OpenSLX::LibScanner;
use OpenSLX::OSPlugin::Roster;
use OpenSLX::Utils;

################################################################################
### interface methods
################################################################################
sub new
{
    my $class  = shift;
    my $params = shift || {};

    checkParams($params, { 
        'attrs'          => '!',
        'debug-level'    => '?',
        'export-name'    => '!',
        'export-uri'     => '!',
        'initramfs'      => '!',
        'kernel-params'  => '!',
        'kernel-version' => '!',
        'plugins'        => '!',
        'root-path'      => '!',
        'slx-version'    => '!',
        'system-name'    => '!',
        'preboot-id'     => '?',
        'boot-uri'       => '?',
    } );

    my $self = $params;

    $self->{'system-name'} =~ m{^([^\-]+)-([^:\-]+)}
        or die "unable to extract distro-info from $self->{'system-name'}!";

    $self->{'distro-name'} = lc($1);
    $self->{'distro-ver'} = $2;
    
    my $fullDistroName = lc($1) . '-' . $2;

    $self->{distro} = loadDistroModule({
        distroName  => $fullDistroName,
        distroScope => 'OpenSLX::MakeInitRamFS::Distro',
    });
    if (!$self->{distro}) {
        die _tr(
            'unable to load any MakeInitRamFS::Distro module for system %s!',
            $self->{'system-name'}
        );
    }
    
    $self->{'lib-scanner'} 
        = OpenSLX::LibScanner->new({ 'root-path' => $self->{'root-path'} });
    
    $self->{'suggested-kernel-modules'} = [];
    $self->{'filtered-kernel-modules'}  = [];

    return bless $self, $class;
}

sub execute
{
    my $self   = shift;
    my $dryRun = shift;

    $self->_collectCMDs();

    vlog(1, _tr("creating initramfs '%s' ...", $self->{'initramfs'}));
    $self->_executeCMDs() unless $dryRun;

    return;
}

sub haveKernelParam
{
    my $self  = shift;
    my $param = shift;
    
    return ref $param eq 'Regexp'
        ? grep { $_ =~ $param } @{ $self->{'kernel-params'} }
        : grep { $_ eq $param } @{ $self->{'kernel-params'} };
}

sub addKernelParams
{
    my $self = shift;
    
    push @{ $self->{'kernel-params'} }, @_;
    
    return;
}

sub kernelParams
{
    my $self = shift;
    
    return @{ $self->{'kernel-params'} };
}

sub addKernelModules
{
    my $self = shift;
    
    push @{ $self->{'suggested-kernel-modules'} }, @_;
    
    return;
}

################################################################################
### implementation methods
################################################################################
sub _executeCMDs
{
    my $self = shift;
    
    foreach my $cmd (@{$self->{CMDs}}) {
        if (ref($cmd) eq 'HASH') {
            vlog(3, "writing $cmd->{file}");
            my $flags = defined $cmd->{mode} ? { mode => $cmd->{mode} } : undef;
            spitFile($cmd->{file}, $cmd->{content}, $flags);
        }
        else {
            vlog(3, "executing: $cmd");
            if (slxsystem($cmd)) {
                die _tr(
                    "unable to execute shell-cmd\n\t%s", $cmd
                );
            }
        }
    }

    return;
}

sub addCMD
{
    my $self = shift;
    my $cmd  = shift;
    
    push @{$self->{CMDs}}, $cmd;

    return;
}
    
sub _findBinary
{
    my $self   = shift;
    my $binary = shift;
    
    my @binDirs = qw(
        bin sbin usr/bin usr/sbin usr/local/bin usr/local/sbin usr/bin/X11
    );
    foreach my $binDir (@binDirs) {
        my $binPath = "$self->{'root-path'}/$binDir/$binary";
        return $binPath if -f $binPath && -x $binPath;
    }
    
    return;
}
    
sub _addFilteredKernelModules
{
    my $self   = shift;

    push @{ $self->{'filtered-kernel-modules'} }, @_;

    return;
}

sub _copyKernelModules
{
    my $self = shift;
    
    # read modules.dep and use it to determine module dependencies
    my $sourcePath = "$self->{'root-path'}/lib/modules/$self->{'kernel-version'}";
    my @modulesDep = slurpFile("$sourcePath/modules.dep")
        or die _tr('unable to open %s!', "$sourcePath/modules.dep");
    my (%dependentModules, %modulePath, %modulesToBeCopied);
    foreach my $modulesDep (@modulesDep) { 
        next if $modulesDep !~ m{^(.+?)/([^/]+)\.ko:\s*(.*?)\s*$};
        my $path = $1;
        if (substr($path, 0, 5) ne '/lib/') {
            # some distros (e.g. ubuntu-9) use a local path instead of an
            # absolute path, we need to make it absolute:
            $path = "/lib/modules/$self->{'kernel-version'}/$path";
        }
        my $module = $2;
        my $dependentsList = $3;
        my $fullModulePath = "$path/$module.ko";
        $modulePath{$module} = [] if !exists $modulePath{$module};
        push @{$modulePath{$module}}, $fullModulePath;
        $dependentModules{$fullModulePath} = [
            map {
                if (substr($_, 0, 5) ne '/lib/') {
                    # some distros (e.g. ubuntu-9) use a local path instead of an
                    # absolute path, we need to make it absolute:
                    $_ = "/lib/modules/$self->{'kernel-version'}/$_";
                }
                $_;
            }
            split ' ', $dependentsList
        ];
    }

    my $targetPath 
        = "$self->{'build-path'}/lib/modules/$self->{'kernel-version'}";
    $self->addCMD("mkdir -p $targetPath");
    $self->addCMD("cp -p $sourcePath/modules.* $targetPath/");
    
    # add a couple of kernel modules that we expect to be used in stage3
    # (some of these modules do not exist on all distros, so they will be
    # filtered out again by the respective distro object):
    my @kernelModules = qw(
        af_packet unix hid hid-bright usbhid uhci-hcd ohci-hcd vesafb fbcon netconsole
    );
    push @kernelModules, @{ $self->{'suggested-kernel-modules'} };

    push @kernelModules, split ' ', $self->{attrs}->{ramfs_fsmods};
    push @kernelModules, split ' ', $self->{attrs}->{ramfs_miscmods};
    push @kernelModules, split ' ', $self->{attrs}->{ramfs_nicmods};

    if ($self->{attrs}->{ramfs_nicmods} =~ m{virtio}i) {
        push @kernelModules, qw( virtio_pci virtio_net );
    }

    # a function that determines dependent modules recursively
    my $addDependentsSub;
    $addDependentsSub = sub {
        my $modulePath = shift;
        foreach my $dependentModule (@{$dependentModules{$modulePath}}) {
            next if $modulesToBeCopied{$dependentModule};
            $modulesToBeCopied{$dependentModule} = 1;
            $addDependentsSub->($dependentModule);
        }
    };

    # start with the given kernel modules (names) and build a list of all
    # required modules
    foreach my $kernelModule (@kernelModules) {
        if (!$modulePath{$kernelModule}) {
            if (! grep { $_ eq $kernelModule    } 
                @{ $self->{'filtered-kernel-modules'} }
            ) {
                warn _tr(
                    'kernel module "%s" not found (in modules.dep)', 
                    $kernelModule
                );
            }
        }
        foreach my $modulePath (@{$modulePath{$kernelModule}}) {
            next if $modulesToBeCopied{$modulePath};
            $modulesToBeCopied{$modulePath} = 1;
            $addDependentsSub->($modulePath);
        }
    }
    
    # build a list of required firmwares out of the list of modules - not
    # totally optimal
    my @firmwares;
    $self->addCMD("mkdir -p $self->{'build-path'}/lib/firmware");
    $self->addCMD("ln -s . $self->{'build-path'}/lib/firmware/$self->{'kernel-version'}");
    foreach my $moduleToBeCopied(%modulesToBeCopied) {
    	$moduleToBeCopied =~ /.*\/(.*?)$/;
        # implies usage of switch
        vlog(1,$1);
        switch ($1){
            # nic modules fw
            case "bnx2.ko" {push @firmwares, split ' ', "bnx2/"}
            case "bnx2x.ko" {push @firmwares, split ' ', "bnx2x/"}
            case "e100.ko" {push @firmwares, split ' ', "e100"}
            case "r8169.ko" {push @firmwares, split ' ', "rtl_nic/"}
            case "tg3.ko" {push @firmwares, split ' ', "tigon/"}
            # wifi fw
            case "iwlwifi" {
                push @firmwares, split ' ',
                "iwlwifi-3945-1.ucode iwlwifi-3945-2.ucode iwlwifi-4965-1.ucode iwlwifi-4965-2.ucode iwlwifi-5000-1.ucode"
            }
            # modules required for graphic adaptors (bootsplash, Xorg)
            case "radeon.ko" {push @firmwares, split ' ', "radeon/"}
            case "mga.ko" {push @firmwares, split ' ', "matrox/"}
            case "r128.ko" {push @firmwares, split ' ', "r128/"}
        }
    }
    # copy all the firmwares that we think are required
    foreach my $firmwareToBeCopied (@firmwares) {
        my $source = followLink(
            "$self->{'root-path'}/lib/firmware/$self->{'kernel-version'}/$firmwareToBeCopied", $self->{'root-path'}
        );
        my $alternative_source = followLink(
            "$self->{'root-path'}/lib/firmware/$firmwareToBeCopied", $self->{'root-path'}
        );
        my $target = "$self->{'build-path'}/lib/firmware/";
        if (-e $source){
        	$self->addCMD("cp -pr --dereference $source $target");
        }
        elsif (-e $alternative_source){
        	$self->addCMD("cp -pr --dereference $alternative_source $target");
        }
        else {
            vlog(3,"unable to find $source for copying purposes");
        }
    }	
    
    # copy all the modules that we think are required
    foreach my $moduleToBeCopied (sort keys %modulesToBeCopied) {
        my $source = followLink(
            "$self->{'root-path'}$moduleToBeCopied", $self->{'root-path'}
        );
        my $target = "$self->{'build-path'}$moduleToBeCopied";
        my ($targetdir) = $target =~m/(.*\/).*$/;
        vlog(5,"Trying to make directory: $targetdir");
        $self->addCMD("mkdir -p $targetdir");
        $self->addCMD("cp -p --dereference $source $target");
    }
    
    return;
}

sub _platformSpecificFileFor
{
    my $self   = shift;
    my $binary = shift;

    if ($self->{'system-name'} =~ m{64}) {
        return $binary . '.x86_64';
    }
    return $binary . '.i586';
}

sub _writeInitramfsSetup
{
    my $self = shift;
    
    # generate initramfs-setup file containing attributes that are
    # relevant for the initramfs only (before there's a root-FS):
    my $initramfsAttrs = {
        'host_name'      => 'slx-client', # just to have something at all
        'ramfs_fsmods'   => $self->{attrs}->{ramfs_fsmods} || '',
        'ramfs_miscmods' => $self->{attrs}->{ramfs_miscmods} || '',
        'ramfs_nicmods'  => $self->{attrs}->{ramfs_nicmods} || '',
        'ramfs_firmmods'  => $self->{attrs}->{ramfs_firmmods} || '',
        'rootfs'         => $self->{'export-uri'} || '',
        'hw_local_disk'  => $self->{attrs}->{hw_local_disk} || '',
    };
    my $content = "# attributes set by slxconfig-demuxer:\n";
    foreach my $attr (keys %$initramfsAttrs) {
        $content .= qq[$attr="$initramfsAttrs->{$attr}"\n];
    }
    $self->addCMD( {
        file    => "$self->{'build-path'}/etc/initramfs-setup", 
        content => $content
    } );
    
    return;
}

sub _writeSlxSystemConf
{
    my $self = shift;
    
    # generate slxsystem.conf file with variables that are needed
    # in stage3 init.
    # TODO: either put this stuff into initramfs-setup or find another solution
    my $date = strftime("%d.%m.%Y", localtime);
    my $slxConf = unshiftHereDoc(<<"    End-of-Here");
        slxconf_date=$date
        slxconf_kernver=$self->{'kernel-version'}
        slxconf_listnwmod="$self->{attrs}->{ramfs_nicmods}"
        slxconf_distro_name=$self->{'distro-name'}
        slxconf_distro_ver=$self->{'distro-ver'}
        slxconf_system_name=$self->{'system-name'}
        slxconf_slxver="$self->{'slx-version'}"
    End-of-Here
    $self->addCMD( {
        file    => "$self->{'build-path'}/etc/slxsystem.conf", 
        content => $slxConf
    } );
    
    # check if default directories available and copy them to /etc
    my $defaultDirConfig = "$self->{'root-path'}/etc/opt/openslx/openslx.conf";
    my $configTargetPath = "$self->{'build-path'}/etc";
    #my $defaultConfVer   = slurpFile("$defaultDirConfig");
    #my $actConfVer       = "Version 0.2";

    if (-r $defaultDirConfig) {
        $self->addCMD("cp -p $defaultDirConfig $configTargetPath/");
#        if ($defaultConfVer =~ m{$actConfVer}) {
#            warn _tr(
#                "Your version of default dir file (openslx.conf) is to old!\n".
#                "Eventually the system won't work.\n" .
#                "Please run install, update or clone of this system again!\n");
#        }
    } else {
        die _tr(
            "No default directories defined!\n" .
            "Please run install, update or clone of this system again!\n");
    }

    return;
}

sub _calloutToPlugins
{
    my $self = shift;

    my $pluginInitdPath = "$self->{'build-path'}/etc/plugin-init.d";
    my $initHooksPath   = "$self->{'build-path'}/etc/init-hooks";
    $self->addCMD("mkdir -p $pluginInitdPath $initHooksPath");

    foreach my $pluginName (@{$self->{'plugins'}}) {
        my $plugin = OpenSLX::OSPlugin::Roster->getPlugin($pluginName);
        next if !$plugin;

        # create a hash only containing the attributes relating to the
        # current plugin 
        my $allAttrs = $self->{attrs};
        my %pluginAttrs;
        for my $attrName (grep { $_ =~ m{^${pluginName}::} } keys %$allAttrs) {
            $pluginAttrs{$attrName} = $allAttrs->{$attrName};
        }

        # let plugin setup itself in the initramfs
        $plugin->setupPluginInInitramfs(\%pluginAttrs, $self);
    }
    return;
}

sub _createInitRamFS
{
    my $self = shift;

    my $buildPath = $self->{'build-path'};
    $self->addCMD("chroot $buildPath ldconfig");
    $self->addCMD(
        "cd $buildPath "
        . "&& find . "
            . "| cpio -H newc --quiet --create "
            . "| gzip -9 >$self->{initramfs}"
    );

    return;
}

1;