summaryrefslogblamecommitdiffstats
path: root/initramfs/OpenSLX/MakeInitRamFS/Engine.pm
blob: 6ce692d6947a5019ff1fa4790dd93535aa56b1e6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                               
                   

               
                       

                    
                        
                              

                   




                                                                       









                                                                                
                                        


                                        
                                        








                                        







                                                                                    
                                   





                                                                        
        


                                                                                    


                                                 
 




                                   









                                             


































                                                                      




                                                                                
                         

                           


                                 
                                            

                                      
                                     


                                          








                                      

                                             

                                          


                                         
 
                                   
 

                                             

                                    
                                   

                                  
 


               










                                                                                            
                                                   










                                                                                 
          








                                    




                                                                    
                                           




                            
                              











                                 
                      

                                                                             
                                                      


















                                                                               
                        


                                                                        





                                                                                    
                        


                                                                        

                                                                        
                               
                              


                                                                                       













                                                                          

                                                                                                
                                         
                                                                                                 

                                                                    
                                                      

                                                                                               
                                                                    
                                                      

                                                                                            









                                                                                                        

                                                                                                       


                                                                         



                                                                                                      





                                                                   

                                                                                   
                                        
                                                        



                                                                                            









                                      







                                                                                   














                                                                           
                                              
                                                                                       


                                                 

                                                 



                                                           


























                                                                              
                                                                            

                             
                                                                           















                                                                             
                                                                                     

                       
                                                                   




























                                                                                  
                                                                        











                                                         
                                                                                            
                 
                                                                           










                                                      
                                                

                                        
                                                




                                                         


                                                                                          
                 
                                                                           









                                                      




                                                                                
                                                                      
                                           
                                                                                 


                                   
                                                                       











                                                                          
                                             




               









                                             








                                                         





















                                                                                      

                                                                  






                                                                        

                                                                      


















                                                                                 







                                                                                         










                                                                                     
                                                     


                                                                                     
                                                                      
                                                                     
















                                                                           
                                                                           



               











                                              










                                                                           


                                                         



















                                                                                         
                        


                                                                        









                                                                     
                                                                                   









                                                                   
                        






                                                                                 
                     


                         



                                                                        
                                                        

                                                                               
                                                                       



               




                                              
                      
                                


                                                            
          




















                                                                                
# 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'} = $1;
	$self->{'distro-ver'} = $2;

	my %distroMap = (
		'debian' => 'Debian',
		'suse'   => 'SUSE',
		'ubuntu' => 'Ubuntu',
	);
	my $distroModule = $distroMap{$self->{'distro-name'}} || 'Base';
	$self->{distro} = instantiateClass(
		"OpenSLX::MakeInitRamFS::Distro::$distroModule"
	);
	
	$self->{'lib-scanner'} 
		= OpenSLX::LibScanner->new({ 'root-path' => $self->{'root-path'} });
	
	$self->{'required-libs'}            = {};
	$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->_addRequiredFSModsAndTools();
	
	$self->_writeInitramfsSetup();
	$self->_writeSlxSystemConf();

	$self->_copyDistroSpecificFiles();
	$self->_copyInitramfsFiles();
	
	$self->_copyBusybox();
	
	$self->_copyDhcpClient();

	$self->_copyRamfsTools();
	
	$self->_copyRequiredFSTools();

	$self->_copyRequiredLayeredFSTools();

	$self->_copyPreAndPostinitFiles();

	if ($self->{'debug-level'}) {
		$self->_copyDebugTools();
	}

	$self->_calloutToPlugins();

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

	$self->_copyKernelModules();
	
	$self->_copyRequiredLibs();
	
	$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
		etc/sysconfig
		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 configuration into one file
	my $config = slurpFile("$distroSpecsPath/$distroName/config-default");
	$config .= "\n";
	$config .= slurpFile("$distroSpecsPath/$distroName/config-$distroVer");
	$self->addCMD( {
		file    => "$self->{'build-path'}/etc/sysconfig/config",
		content => $config,
	} );
		
	# concatenate default- and distro-specific functions into one file
	my $functions = slurpFile("$distroSpecsPath/$distroName/functions-default");
	$functions .= "\n";
	$functions 
		.= slurpFile("$distroSpecsPath/$distroName/functions-$distroVer");
	$self->addCMD( {
		file    => "$self->{'build-path'}/etc/distro-functions",
		content => $functions,
	} );
	
	my $defaultsPath = "$distroSpecsPath/$distroName/files-default";
	if (-e $defaultsPath) {
		$self->addCMD(
			"cp -a $defaultsPath $self->{'build-path'}/etc/sysconfig/files"
		);
	}
		
	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',
						'COMETCEXCL'  => "XF86Config*\nissue*\nmtab*\nfstab*\n",
						'KERNVER'     => $self->{'kernel-version'},
						# 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 _copyBusybox
{
	my $self = shift;

	$self->_copyPlatformSpecificBinary(
		"$openslxConfig{'base-path'}/share/busybox/busybox", '/bin/busybox'
	);
	
	my $busyboxForHost 
		= "$openslxConfig{'base-path'}/share/busybox/busybox"
			. ( hostIs64Bit() ? '.x86_64' : '.i586' );

	my $busyboxHelp = qx{$busyboxForHost --help};
	if ($busyboxHelp !~ m{defined functions:(.+)\z}ims) {
		die "unable to parse busybox --help output:\n$busyboxHelp";
	}
	my $rawAppletList = $1;
	my @busyboxApplets 
		=	map {
				$_ =~ s{\s+}{}igms;
				$_;
			}
			split m{,}, $rawAppletList;
	foreach my $applet (@busyboxApplets) {
		$self->addCMD("ln -sf /bin/busybox $self->{'build-path'}/bin/$applet");
	}
	
	# fake the sh link in busybox environment
	my $shFake = "#!/bin/ash\n/bin/ash \$\@";
	$self->addCMD( {
		file    => "$self->{'build-path'}/bin/sh", 
		content => $shFake,
		mode    => 0755
	} );

	return;
}

sub _copyRamfsTools
{
	my $self = shift;
	
	my @ramfsTools = qw(ddcprobe 915resolution);
	foreach my $tool (@ramfsTools) {
		$self->_copyPlatformSpecificBinary(
			"$openslxConfig{'base-path'}/share/ramfstools/$tool", 
			"/bin/$tool"
		);
	}
	
	return;
}
	
sub _copyDebugTools
{
	my $self = shift;
	
	my @debugTools = qw(strace);
	foreach my $tool (@debugTools) {
		my $toolPath = $self->_findBinary($tool);
		if (!$toolPath) {
			warn _tr('debug-tool "%s" is not available', $tool);
			next;
		}
		$self->addCMD("cp -p $toolPath $self->{'build-path'}/bin");
		$self->_addRequiredLibsFor($toolPath);
	}
	
	return;
}
	
sub _copyDhcpClient
{
	my $self = shift;
	
	# TODO: instead of using dhclient, we should check if the client
	#       provided by busybox still does not support fetching NIS stuff
	#       (and implement that if it doesn't)

	my $toolPath = $self->_findBinary('dhclient');
	if (!$toolPath) {
		warn _tr('tool "dhclient" is not available, using "udhcpc" instead');
		return;
	}
	$self->addCMD("cp -p $toolPath $self->{'build-path'}/bin");
	$self->_addRequiredLibsFor($toolPath);
	
	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 _copyPlatformSpecificBinary
{
	my $self       = shift;
	my $binaryPath = shift;
	my $targetPath = shift;

	my $binary = $self->_platformSpecificFileFor($binaryPath);
	
	$self->addCMD("cp -p $binary $self->{'build-path'}$targetPath");
	$self->_addRequiredLibsFor($binary);

	return;
}

sub _copyRequiredFSTools
{
	my $self = shift;

	foreach my $tool (@{$self->{'fs-tools'}}) {
		my $toolPath = $self->_findBinary($tool);
		if (!$toolPath) {
			die _tr('filesystem-tool "%s" is not available, giving up!', $tool);
		}
		$self->addCMD("cp -p $toolPath $self->{'build-path'}/bin");
		$self->_addRequiredLibsFor($toolPath);
	}

	return;
}

sub _copyRequiredLayeredFSTools
{
	my $self = shift;

	my @tools;
	if ($self->haveKernelParam('unionfs')) {
		push @tools, 'unionctl';
	}
	if ($self->haveKernelParam('cowloop')) {
		push @tools, 'cowdev';
	}
	foreach my $tool (@tools) {
		my $toolPath = $self->_findBinary($tool);
		if (!$toolPath) {
			die _tr(
				'layered-fs-tool "%s" is not available, giving up!', $tool
			);
		}
		$self->addCMD("cp -p $toolPath $self->{'build-path'}/bin");
		$self->_addRequiredLibsFor($toolPath);
	}

	return;
}

sub _copyRequiredLibs
{
	my $self = shift;

	# separate 64-bit libs from 32-bit libs and copy them into different
	# destination folders
	my @libs64 = grep { $_ =~ m{/lib64/} } keys %{$self->{'required-libs'}};
	my @libs32 = grep { $_ !~ m{/lib64/} } keys %{$self->{'required-libs'}};
	if (@libs64) {
		$self->addCMD("mkdir -p $self->{'build-path'}/lib64");
		foreach my $lib (@libs64) {
			$self->addCMD("cp -p $lib $self->{'build-path'}/lib64/");
		}
	}
	foreach my $lib (@libs32) {
		$self->addCMD("cp -p $lib $self->{'build-path'}/lib/");
	}

	return;
}

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

	my @libs = $self->{'lib-scanner'}->determineRequiredLibs($binary);
	foreach my $lib (@libs) {
		$self->_addRequiredLib($lib);
	}

	return;
}

sub _addRequiredLib
{
	my $self = shift;
	my $lib  = shift;

	$self->{'required-libs'}->{$lib} = 1;

	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 _addRequiredFSModsAndTools
{
	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;
	
	my @fsTools = $osExportEngine->requiredFSTools();
	$self->{'fs-tools'} = \@fsTools;

	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/sysconfig/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;
		$plugin->setupPluginInInitramfs($self->{attrs}, $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