# 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;