summaryrefslogblamecommitdiffstats
path: root/initramfs/OpenSLX/MakeInitRamFS/Engine.pm
blob: fbcf35a0c847a660e107fef10f3e1513cbb21a14 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                               
                                                       





                                                                               
                   

               
                       

                    
                        
                              

                   
                                                                       


                                               
 




                                                                                





















                                                                            
                                    
                               

                                           
 








                                                                             
     



                                                                            



                                             



           

                       
 
                          
 
                                         
 
           

 

                   





                                                              



                   




                                           



                


                                         



                    




                                                      

 




                                                                                


                       
 
                             
 
                                


                                  
 
                               


                                      
                                      
 
                               
 
                                         
 

                                
                              
 
           

 

                


















                                                                                

 
          
 



                                
 
           
 
    

                   









                                                                




















                                                                     

                            






                                                                           


                                                                                



                                                            




                                                                










                                                                        
             



                       




























                                                                                






























                                                                               

 

               













                                                                          

                             
                       
 
                                                     
 
           

 

                      
























                                                                                  
                                             



















































                                                                             



                            
                     
 



                                                         
                                                         





                                                                   

 

                            

                       
 



                                          

 
                      
 









                                                                       
           



                        




















                                                                            



                       















                                                                               
                                                               



                           

 
                     
 
                     
 


                                                                    
 


                                                                       










                                                                               

           

 

                    
                     
 






                                                
 
           

















                                                                                
# Copyright (c) 2006, 2007 - 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.pm
#    - provides driver engine for MakeInitialRamFS API.
# -----------------------------------------------------------------------------
package OpenSLX::MakeInitRamFS::Engine;

use strict;
use warnings;

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

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

# TODO: implement support for the following (either here or as plugin):
#            wlan
#            tpm
#            cdboot (must be implemented here!)

################################################################################
### 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'    => '!',
    } );

    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();

    $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 _collectCMDs
{
    my $self = shift;
    
    $self->{CMDs} = [];

    $self->_setupBuildPath();

    $self->_addRequiredFSMods();
    
    $self->_writeInitramfsSetup();
    $self->_writeSlxSystemConf();

    $self->_copyUclibcRootfs();
    $self->_copyDistroSpecificFiles();
    $self->_copyInitramfsFiles();
    
    $self->_copyPreAndPostinitFiles();

    $self->_calloutToPlugins();

    $self->{distro}->applyChanges($self);

    $self->_copyKernelModules();
    
    $self->_createInitRamFS();

    return;
}

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 _setupBuildPath
{
    my $self = shift;
    
    my $buildPath = "$openslxConfig{'temp-path'}/slx-initramfs";
    $self->addCMD("rm -rf $buildPath");

    my @stdFolders = qw(
        bin 
        dev 
        etc
        etc/init-hooks
        lib
        mnt 
        proc 
        root 
        sys 
        tmp 
        usr/share
        var/lib
        var/lib/nfs/state
        var/run
    );
    $self->addCMD(
        'mkdir -p ' . join(' ', map { "$buildPath/$_"; } @stdFolders)
    );
    $self->addCMD("ln -sfn /bin $buildPath/sbin");
    
    $self->{'build-path'} = $buildPath;
    
    return;
}
    
sub _copyDistroSpecificFiles
{
    my $self = shift;

    my $distroSpecsPath = "$openslxConfig{'base-path'}/share/distro-specs";

    my $distroName = $self->{'distro-name'};
    my $distroVer = $self->{'distro-ver'};
    
    # concatenate default- and distro-specific functions into one file
    my $functions = slurpFile("$distroSpecsPath/$distroName/functions-default");
    $functions .= "\n";
    $functions .= slurpFile(
        "$distroSpecsPath/$distroName/functions-$distroVer",
        { failIfMissing => 0 }
    );
    $self->addCMD( {
        file    => "$self->{'build-path'}/etc/distro-functions",
        content => $functions,
    } );
    
    return 1;
}

sub _copyUclibcRootfs
{
    my $self = shift;

    my $uclibcRootfs = "$openslxConfig{'base-path'}/share/uclib-rootfs";

    $self->addCMD("rsync -rlpt $uclibcRootfs/ $self->{'build-path'}");
    
    return 1;
}

sub _copyInitramfsFiles
{
    my $self = shift;

    my $initramfsPath = "$openslxConfig{'base-path'}/share/initramfs";

    find(
        {
            wanted => sub {
                my $len = length($initramfsPath);
                my $file = $File::Find::name;
                my $relName = length($file) > $len ? substr($file, $len+1) : '';
                if (-d) {
                    $self->addCMD("mkdir -p $self->{'build-path'}/$relName");
                } elsif (-l $file) {
                    my $target = readlink $file;
                    $self->addCMD(
                        "ln -sf $target $self->{'build-path'}/$relName"
                    );
                } elsif (qx{file $file} =~ m{ELF}) {
                    $self->addCMD(
                        "cp -p $file $self->{'build-path'}/$relName"
                    );
                } else {
                    my $text = slurpFile($file, { 'io-layer' => 'bytes' } );

                    # replace macros
                    # TODO: find out what these mean and maybe find a
                    #       different, better solution
                    my %macro = (
                        'COMDIRINDXS' => '/tmp/scratch /var/lib/nobody',
                        # keep serverip as it is (it is handled by init itself)
                        'serverip'    => '@@@serverip@@@',
                    );
                    $text =~ s{\@\@\@([^\@]+)\@\@\@}{
                        if (!exists $macro{$1}) {
                            warn _tr(
                                'unknown macro @@@%s@@@ found in %s', 
                                $1, $File::Find::name
                            );
                            '';
                        } else {
                            $macro{$1};
                        }
                    }eogms;
                    
                    # force shebang with ash
                    $text =~ s{\A#!\s*/bin/.+?$}{#!/bin/ash}ms;
                    
                    $self->addCMD( {
                        file    => "$self->{'build-path'}/$relName",
                        content => $text,
                        mode    => (-x $file ? 0755 : undef),
                    } );
                }
            },
            no_chdir => 1,
        },
        $initramfsPath
    );

    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;
        my $module = $2;
        my $dependentsList = $3;
        my $fullModulePath = "$path/$module.ko";
        $modulePath{$module} = [] if !exists $modulePath{$module};
        push @{$modulePath{$module}}, $fullModulePath;
        $dependentModules{$fullModulePath} = [ split ' ', $dependentsList ];
    }

    my $targetPath 
        = "$self->{'build-path'}/lib/modules/$self->{'kernel-version'}";
    $self->addCMD("mkdir -p $targetPath");
    $self->addCMD("cp -p $sourcePath/modules.* $targetPath/");
    
    # TODO: find out what's the story behing the supposedly required
    #       modules 'af_packet', 'unix' and 'hid' (which seem to be
    #       missing at least on some systems)
    my @kernelModules = qw(
        af_packet unix hid usbhid uhci-hcd ohci-hcd
    );
    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};

    # 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);
        }
    }
    
    # copy all the modules that we think are required
    foreach my $moduleToBeCopied (sort keys %modulesToBeCopied) {
        my $targetDir = "$self->{'build-path'}" . dirname($moduleToBeCopied);
        $self->addCMD("mkdir -p $targetDir");
        my $source = followLink(
            "$self->{'root-path'}$moduleToBeCopied", $self->{'root-path'}
        );
        my $target = "$self->{'build-path'}$moduleToBeCopied";
        $self->addCMD("cp -p --dereference $source $target");
    }
    
    return;
}

sub _copyPreAndPostinitFiles
{
    my $self = shift;

    foreach my $cfg (
        'default/initramfs/preinit.local',
        "$self->{'system-name'}/initramfs/preinit.local",
        'default/initramfs/postinit.local',
        "$self->{'system-name'}/initramfs/postinit.local"
    ) {
        my $cfgPath = "$openslxConfig{'private-path'}/config/$cfg";
        next if !-f $cfgPath;
        $self->addCMD("cp -p $cfgPath $self->{'build-path'}/bin/");
    }
    return;
}

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

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

sub _addRequiredFSMods
{
    my $self = shift;
    
    my $osExportEngine = instantiateClass("OpenSLX::OSExport::Engine");
    $osExportEngine->initializeFromExisting($self->{'export-name'});
    my $fsMods = $self->{attrs}->{ramfs_fsmods} || '';
    foreach my $fsMod ($osExportEngine->requiredFSMods()) {
        $fsMods .= " $fsMod" if $fsMods !~ m{$fsMod};
    }
    $self->{attrs}->{ramfs_fsmods} = $fsMods;
    
    return;
}

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} || '',
        'rootfs'         => $self->{'export-uri'} || '',
    };
    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
    } );

    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(
        "cd $buildPath "
        . "&& find . "
            . "| cpio -H newc --quiet --create "
            . "| gzip -9 >$self->{initramfs}"
    );

    return;
}

1;
################################################################################

=pod

=head1 NAME

OpenSLX::MakeInitRamFS::Engine

=head1 SYNOPSIS

=head1 DESCRIPTION

...

=cut