summaryrefslogblamecommitdiffstats
path: root/initramfs/OpenSLX/MakeInitRamFS/Engine.pm
blob: 925fa382f8149df6fef2994f0f81b4b6f8fc0eae (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 OpenSLX::Basics;
use OpenSLX::LibScanner;
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',
		'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'} = {};

	return bless $self, $class;
}

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

	$self->_collectCMDs();

	$self->_executeCMDs() unless $dryRun;

	return;
}

################################################################################
### implementation methods
################################################################################
sub _collectCMDs
{
	my $self = shift;
	
	$self->{CMDs} = [];

	$self->_setupBuildPath();

	$self->_addRequiredFSModsAndTools();
	
	$self->_writeInitramfsSetup();

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

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

	$self->_copyRequiredLayeredFSTools();

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

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

#	foreach my $plugin (@{$self->{'plugin-instances'}}) {
#		$plugin->specifyInitramfsAttrs($initramfsAttrs);
#	}

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

	$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(0, "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/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 sh shebang over to ash
					$text =~ s{^#!\s*/bin/sh}{#!/bin/ash};
					
					$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 @busyboxApplets = qw(
		ar arping ash bunzip2 cat chmod chown chroot cp cpio cut
	    date dd df dmesg du echo env expr fdisk free grep gunzip hwclock
	    insmod id ip kill killall ln ls lsmod mdev mkdir mknod mkswap 
	    modprobe mount mv nice ping printf ps rdate rm rmmod sed sleep 
	    sort swapoff swapon switch_root tar test tftp time touch tr 
	    udhcpc umount uptime usleep vconfig vi wget zcat zcip
	);
	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 "$tool" is not available, giving up!');
		}
		$self->_addCMD("cp -p $toolPath $self->{'build-path'}/bin");
		$self->_addRequiredLibsFor($toolPath);
	}

	return;
}

sub _copyRequiredLayeredFSTools
{
	my $self = shift;

	my @tools;
	if ($self->{'kernel-params'} =~ m{\bunionfs\b}) {
		push @tools, 'unionctl';
	}
	if ($self->{'kernel-params'} =~ m{\bcowloop\b}) {
		push @tools, 'cowdev';
	}
	foreach my $tool (@tools) {
		my $toolPath = $self->_findBinary($tool);
		if (!$toolPath) {
			die _tr('layered-fs-tool "$tool" is not available, giving up!');
		}
		$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 _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, 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}) {
			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 = "$self->{'root-path'}$moduleToBeCopied";
		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 _createInitRamFS
{
	my $self = shift;

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

	return;
}

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

=pod

=head1 NAME

OpenSLX::MakeInitRamFS::Engine

=head1 SYNOPSIS

=head1 DESCRIPTION

...

=cut