#!/usr/bin/perl
#
# LiveCD iso build script
#
# Copyright (C) 2002-2004, Jaco Greeff <jaco@puxedo.org>
# Copyright (C) 2003, Buchan Milne <bgmilne@obsidian.co.za>
# Copyright (C) 2004, Tom Kelly  <tom_kelly33@yahoo.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Adapted from the MiniCD (http://www.linuxminicd.org) and mkinitrd build scripts
#
# $Id: mklivecd.in,v 1.165 2008/04/12 09:31:05 ikerekes Exp $
#

use strict;
use lib qw(/usr/lib/libDrakX);

### dependancies
use Getopt::Long;
use MDK::Common;
use run_program;
use Storable qw(store);
use threads::shared;

### useful functions
sub get_exec { my $r = qx($_[0]); chomp($r); $r; };

### global "constants"
my $PROG_VERSION   = '0.7.1';
my $PROG_NAME      = 'mklivecd';
my $ESC            = "\x1B[";
my $URL            = "http://livecd.berlios.de/";
my $COPYRIGHT      = "Copyright (C) 2002-2004, Jaco Greeff <jaco\@puxedo.org>";

### global variables
my $starttime      = time;
my $workdir        = undef;
my $kernel26       = undef;
my $kernelver      = "2.6.20";
my $dietarch       = undef;
my $depmod         = "/sbin/depmod";
my $modfile        = "/etc/modules.conf";
my $modext         = ".ko";
my $modules        = undef;
my $modules_opt    = undef;
my $modules_26     = 'nls_iso8859-1 nls_iso8859-2 nls_cp437 jbd ext3 unionfs reiserfs xfs fat msdos vfat ntfs nls_utf8 isofs cdrom ide-core ide-cd i2c-core i2c-piix4 ff-memless evdev dock crc-t10dif squashfs loop paride parport  zlib_deflate ahci ata_generic ata_piix pata_ali pata_amd pata_artop pata_atiixp pata_cmd640 pata_cmd64x pata_cs5520 pata_cs5530 pata_cs5535 pata_cypress pata_efar pata_hpt366 pata_hpt37x pata_hpt3x2n pata_hpt3x3 pata_isapnp pata_it8213 pata_it821x pata_jmicron pata_legacy pata_marvell pata_mpiix pata_netcell pata_ns87410 pata_oldpiix pata_optidma pata_opti pata_pcmcia pata_pdc2027x pata_pdc202xx_old pata_platform pata_qdi pata_radisys pata_rz1000 pata_sc1200 pata_serverworks pata_sil680 pata_sis pata_sl82c105 pata_triflex pata_via pata_winbond pdc_adma sata_inic162x sata_mv sata_nv sata_promise sata_qstor sata_sil24 sata_sil sata_sis sata_svw sata_sx4 sata_uli sata_via sata_vsc 3w-9xxx 3w-xxxx a100u2w aacraid advansys aha152x aha1542 aic79xx aic7xxx aic7xxx_old aic94xx arcmsr atp870u BusLogic ch dc395x dmx3191d dpt_i2o dtc eata fdomain gdth g_NCR5380 g_NCR5380_mmio hptiop ide-scsi imm in2000 initio ipr ips lpfc megaraid megaraid_mbox megaraid_sas NCR53c406a nsp32 osst pas16 ppa psi240i qla1280 qla2xxx qla4xxx qlogicfas raid_class seagate stex st sym53c416 sym53c8xx tmscsim u14-34f ultrastor wd7000 sx8  3w-xxxx scsi_mod sr_mod libata sg sd_mod scsi_transport_fc scsi_transport_sas scsi_transport_spi mptbase mptscsih i2o_core megaraid_mm qlogicfas408 usbcore usbhid uhci-hcd ohci-hcd ehci-hcd usb-storage';
my $modules_opt_26 = 'zlib_deflate 3w-xxxx usbhid uhci-hcd ohci-hcd ehci-hcd usb-storage';
my $nodirs         = '^/[.].* ^/dev$ ^/initrd$ ^/lost+found$ ^/mnt$ ^/media$ ^/proc$ ^/sys$ ^/tmp$ ^/var/tmp$ ^/root/tmp$ ^/proc/asound';
my $nofiles        = '^/[.].* ^/fastboot$ /core[.][0-9][0-9]*$ .*~$ .*[.]rpmnew$ .*[.]rpmsave$ [.]bash_history$ [.]fonts[.]cache-[0-9]$ [.]xauth.* [.]wh[.]* [.]xsession-errors$ ^/var/run/ ^/var/lock/subsys/ ^/var/lib/dhcp/ ^/etc/modprobe.conf ^/etc/sysconfig/network-scripts/ifcfg-eth* ^/etc/udev/rules.d/61-* ^/etc/asound.state ^/etc/fstab ^/etc/X11/xorg.conf ^/etc/lilo.conf ^/boot/grub/menu.lst ^/etc/modprobe.preload.d/cpufreq';
my $loopmod        = undef;
my $loopcmp        = undef;
my $o_stage2       = undef;
my $finaliso       = "livecd.iso";
my $loader         = "isolinux/isolinux.bin";
my $md5sum	   = undef;
my $genisoimage_opts   = "";	# Fix for concat of undef in iso looptype

### command-line options
my $o_verbose;
my $o_workdir;
my $o_root         = "/";
my $o_tmp          = "/tmp";
my $o_looptype     = "sqfs";
my $o_keyboard     = 'us';
my $o_resolution   = '800x600';
my $o_vgamode      = '788';
my $o_splash       = "silent";
my $o_bootloader   = "iso";
my $o_ufs          = 'aufs';
my $o_kernel       = get_exec("uname -r");
my $o_mbkopt       = "";
my $o_timeout      = 150;
my %opts           = ( # these are all the options with defaults
	'root'       => \$o_root,
	'tmp'        => \$o_tmp,
	'keyboard'   => \$o_keyboard,
	'resolution' => \$o_resolution,
	'splash'     => \$o_splash,
	'kernel'     => \$o_kernel,
	'timeout'    => \$o_timeout,
	'bootloader' => \$o_bootloader,
	'mbkopt'     => \$o_mbkopt,
	'ufs'        => \$o_ufs
);


### startup banner
sub print_banner {
	print STDERR "$PROG_NAME, version $PROG_VERSION, $URL
$COPYRIGHT

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
";
}


### usage
sub print_usage {
	print STDERR "Usage:
   $0 [options] <livecd-image>

General Options:
   --help                          Display this message
   --version                       Display version information
   --verbose                       Be verbose in output
   --noclean                       Don't remove temporary files on exit. #'
   --workdir			   Specify a working directory which will not
   				   be cleaned.
   --debug                         Display some extra debugging information
                                   while building the CD. (Usefull for bug
                                   reports to the developers.)

Image generation:
   --root <rootdir>                Root directory of the live filesystem to use
                                   as the for the image of the LiveCD.
                                   (default: $o_root)
   --tmp <tmpdir>                  Name of the directory to be used for
                                   temporary file storage.
                                   (default: $o_tmp)
   --img <image>                   Name of the saved compressed image. When an
                                   image by this name is found, it will not be
                                   re-created or overwritten, rather the
                                   existing image will be re-used, i.e. the
                                   compressed image is not re-built.
   --nofile <ex1>[,][...]          Excludes files from the final image. (Also
                                   see the --nodir option for a full
                                   description)
   --nodir <ex1>[,][...]           Excludes directories from the final image.
                                   Patterns passed to this option (as with the
                                   --nofile option) should be valid in a grep(1)
                                   search, e.g. --nodir=^/home/jaco,^/root/.mcop
                                   will exclude both the /home/jaco and
                                   /root/.mcop directories from the final
                                   LiveCD.
   --sort <file>                   Sort the files on the compressed iso image
                                   according to the genisoimage-style sort specifier
                                   as detailed in file.
   --kernel <kernel>               Kernel to use as default for the LiveCD
                                   image. (Output of uname -r)
                                   (default: $o_kernel)

Boot options:
   --bootopt <option>              Specify an additional boot option to pass to
                                   the kernel command-line.
   --bootmsg <msg>                 Use 'msg' as the isolinux boot message.
   --bootkey <key=msg>             Display 'msg' on key 'key' from isolinux.
   --bootimg <img>                 Use 'img' (LSS format) as the isolinux.
                                   background display image.
   --bootloader <iso|grub|usb>     The bootloader to use on the livecd i.e. isolinux, 
                                   GRUB or syslinux for usb stick
   --bootmenu <file>               What boot menu definition file should be used
                                   in case that bootloader option is set to iso or grub.
                                   For iso this file must be named 'isolinux.cfg',
				   for grub the name must be 'menu.lst'.
				   Boot menu will be generated if not specified.
   --boottheme <name>              Which gfxboot theme should be used.
                                  Defaults to 'Mandriva'
   --bootlang <lang code>          Which language shound be used as default
                                   in the boot menu.
				   Defaults to 'en'.
   --mbkopt <kernel>               Create the ISO with multi boot kernel option.
   --ufs <unionfs|aufs>            Specify the union file system.
   --timeout <sec>                 Specify the default ISO Linux prompt timeout
                                   in seconds.
                                   (default: $o_timeout)
   --noprompt                      Disable ISO Linux prompt (i.e. prompt 0).
   --keyboard <mapping>            Specify a different keyboard layout as
                                   default for the LiveCD.
                                   (default: $o_keyboard)
   --resolution <res>              Specify the resolution for the framebuffer
                                   output device. (Either resolution or normal)
                                   (default: $o_resolution)
   --splash <silent|verbose|no>    Create the LiveCD with bootsplash support if
                                   available on the root filesystem.
                                   (default: $o_splash)
   --fstab <options>               Override the default options for the fstab on
                                   the LiveCD. Options are one or more of 'auto'
                                   and 'rw', for example '--fstab=rw,auto' will
                                   automatically mount all detected partitions
                                   rw.

ISO Image options:
   --isoextrafiles <path>          Add the files in 'path' to the root of the
                                   LiveCD ISO image.
   --application <id>              Use the specified iso application ID, as '-A'
                                   option to genisoimage.
   --volumeid <id>                 Use the specified iso volume ID, as a '-V'
                                   option to genisoimage.
   --preparer <prep>               Use the specified preparer ID, as a '-p'
                                   option to genisoimage.
   --publisher <pub>               Use the specified publisher ID, as a '-P'
                                   option to genisoimage.
   --md5sum                        Compute and implant the md5sum to verify media.
   
Behaviour:
   --usbhome                       Use USB memory stick devices as a persistent
                                   home when available/connected on bootup.

Examples:
    $0 --nodir ^/usr/src/RPM,^/root/tmp livecd.iso
    $0 --splash=silent livecd.iso

";
	exit(1);
}


### format an elapsed time
sub fmt_elapsed {
	my ($t) = @_;
	my $h = int($t/3600);
	my $m = int(($t - $h*3600)/60);
	my $s = int($t - $h*3600 - $m*60);
	sprintf("%02d:%02d:%02d", $h, $m, $s);
}


### format a number
sub fmt_number {
	my ($n) = @_;
	$n = $n/1000;
	my $t = int($n/1000);
	my $h = $n - $t*1000;
	my $r = sprintf("%0.3f", $h);
	$r =~ s/[.]/,/;
	if ($t > 0) {
		$r = "0$r" while (length($r) < 7);
		$r = "$t,$r";
	}
	$r;
}


### progress bar globals
my $PROGRESS_MAX     = 76;
my $progress_max     : shared = 0;
my $progress_inc     : shared = 0;
my $progress_curr    : shared = 0;
my $progress_start   : shared = 0;
my $progress_tot     : shared = 0;
my $progress_text    : shared = undef;
my $progress_ttext   : shared = undef;
my $progress_timer   : shared = -1; # 0 to active, -1 to deactivate
my $progress_last    : shared = 0;

### to be called before we use the progress bar
sub start_progress {
	my ($title, $max) = @_;
	my $i = 0;
	$| = 1;
	print $title.":\n";
	print "[";
	print " " while ($i++ < $PROGRESS_MAX);
	print "]";
	$progress_max = $max;
	$progress_inc = $progress_max/$PROGRESS_MAX;
	$progress_start = time;
	$progress_curr = 0;
	$progress_tot = 0;
	$progress_text = undef;
	$progress_ttext = undef;
	$progress_last = 0;
	print $ESC."1A".$ESC."52G";
	print "[  0.00% ".fmt_elapsed(0)."/".fmt_elapsed(0)."]";
	start_thread();
}


### while we are progressing
sub set_progress {
	my ($curr, $text, $thr) = @_;
	$progress_last = $curr;
	$progress_last = $progress_max if ($progress_last > $progress_max);
	my $num = int($progress_last/$progress_inc) - $progress_curr;
	if (defined($opts{verbose}) && defined($text)) {
		$text =~ s/\[//g;
		$text =~ s/\]//g;
		$text =~ s/\+//g;
		if (!defined($progress_text) || !($text =~ m/^$progress_text$/)) {
			if (defined($thr) || ($progress_timer == -1)) {
				$progress_text = $text;
				$progress_ttext = $text;
				print "\n";
				$progress_curr += $num;
				print $ESC."1G";
				chomp($text);
				$text =~ s#\r?\n# #gs; # remove newlines in text
				if (length($text) > 72) {
					print "  ".pack("A72", $text)." ...";
				}
				else {
					print "  ".pack("A76",$text);
				}
				print $ESC."1A";
			}
			else {
				$progress_ttext = $text;
			}
		}
	}
	elsif ($num) {
		if (defined($thr) || ($progress_timer == -1)) {
			print "\n";
			my $col = 2+$progress_curr;
			$progress_curr += $num;
			print $ESC.$col."G";
			print "=" while ($num--);
			print $ESC."1A";
		}
	}

	if (defined($thr) || ($progress_timer == -1)) {
		my $elapsed = time - $progress_start;
		my $remain = 0;
		if ($progress_curr < $PROGRESS_MAX) {
			$remain = $progress_curr ? ($elapsed/$progress_curr)*($PROGRESS_MAX - $progress_curr) : 0;
		}
		print $ESC."52G";
		print sprintf("[%6.2f%% ", $curr*100/$progress_max);
		print fmt_elapsed($elapsed)."/".fmt_elapsed($remain+$elapsed)."]";
	}
}


### to close off the progress bar
sub end_progress {
	end_thread();
	set_progress($progress_max, $progress_text);
	print "\n";
	my $i = -2;
	print " " while ($i++ < $PROGRESS_MAX);
	print $ESC."1G";
}


### this is our timer thingy
sub timer_progress {
	while (($progress_timer > 0) && ($progress_timer < 2)) {
		set_progress($progress_last, $progress_ttext, 1);
		sleep(1);
	}
	$progress_timer = 0;
}


### start the progress thread
sub start_thread {
	if ($progress_timer > -1) {
		$progress_timer = 1;
		threads->new(\&timer_progress);
	}
}


### end the progress thread
sub end_thread {
	if ($progress_timer > -1) {
		$progress_timer = 2;
		sleep(2) if ($progress_timer > 0);
	}
}

### parse the command-line options
sub parse_options {
	GetOptions(\%opts,
		'help',
		'verbose+',
		'noclean',
		'workdir=s',
		'debug',
		'root=s',
		'tmp=s',
		'img=s',
		'nofile=s@',
		'nodir=s@',
		'sort=s',
		'kernel=s',
		'lowmem',
		'bootopt=s@',
		'bootmsg=s',
		'bootkey=s%',
		'bootimg=s',
		'timeout=i',
		'noprompt',
		'keyboard=s',
		'resolution=s',
		'splash=s',
		'fstab=s',
		'isoextrafiles=s',
		'application=s',
		'volumeid=s',
		'preparer=s',
		'publisher=s',
		'bootloader=s',
		'mbkopt=s',
		'bootmenu=s',
		'boottheme=s',
		'bootlang=s',
		'ufs=s',
		'md5sum',
		'usbhome'
	);

	# help and stuff
	print_usage if (defined($opts{help}));

	# mandatory stuff
	$o_root =~ s|/$||;
	$o_root .= "/";
	die_("\nFATAL: Root directory (--root) '$o_root' does not exist\n") unless (-d $o_root);
	$o_tmp =~ s|/$||;
	$o_tmp .= "/";
	$o_stage2 = get_exec("find $o_root/lib -name stage2_eltorito");
	die_("\nFATAL: Temporary directory (--tmp) '$o_tmp' does not exist\n") unless (-d $o_tmp);

	# optional stuff
	die_("\nFATAL: Specified sort file (--sort) '".$opts{sort}."' does not exist\n") if (defined($opts{sort}) && !(-f $opts{sort}));
	die_("\nFATAL: Kernel (--kernel) '".$o_kernel."' not installed on the root image. (Directory '".$o_root."/lib/modules/".$o_kernel."' does not exist.)\n") if (!(-d $o_root."/lib/modules/".$o_kernel));
	die_("\nFATAL: Extra ISO directory (--isoextrafiles) '".$opts{isoextrafiles}."' does not exist\n") if (defined($opts{isoextrafiles}) && !(-d $opts{isoextrafiles}));
	die_("\nFATAL: Unknown splash (--splash) option '$o_splash'\n") unless ($o_splash =~ /silent|verbose|no/);
	die_("\nFATAL: Work directory (--workdir) '".$opts{workdir}."' does not exist\n") if (defined($opts{workdir}) && !(-d $opts{workdir}));

	# final iso name
	if (scalar(@ARGV) > 0) {
		die_("\nFATAL: Too many command-line arguments\n") if (scalar(@ARGV) > 1);
		$finaliso = $ARGV[0];
	}
	else {
		#die_("\nFATAL: No final iso name specified\nUse --help for usage\n");
		$finaliso = "livecd.iso";
	}

	# set some options
	my $rhr = "$o_root/etc/redhat-release";
	my $distro  = (-f $rhr) ? get_exec("gawk -F' ' '{ print \$1 \" \" \$2 }' $rhr") : "$PROG_NAME";
	my $version = (-f $rhr) ? get_exec("gawk -F' ' '{ print \$4 \" \" \$5 }' $rhr") : "$PROG_VERSION";
	my $date    = get_exec("date +\%Y\%m\%d\%R");
	$opts{volumeid} = "livecd $date" unless (defined($opts{volumeid}));
	$opts{application} = "$distro $version LiveCD" unless (defined($opts{application}));
	$opts{preparer} = "$PROG_NAME $PROG_VERSION" unless (defined($opts{preparer}));
	$opts{publisher} = "$URL" unless (defined($opts{publisher}));
        $opts{boottheme} = "Mandriva" unless (defined($opts{boottheme}));
	$opts{bootlang} = "en" unless (defined($opts{bootlang}));

	# create our working dir if none given
	if ($opts{workdir}) {
		$workdir = $opts{workdir};
		$opts{noclean} = 1;
	} else {
		$workdir = $o_tmp."mklivecd.$$";
	}
	mkdir_p($workdir);
	die_("\nFATAL: Unable to create working directory, '$workdir'\n") unless (-e $workdir);
	print STDERR "\nWARNING: The temporary directory '$workdir' will not be removed at exit, please do so manually" if (defined($opts{noclean}));

	# massage where we might have options csv
	@{$opts{nofile}} = split(/,/, join(',', @{$opts{nofile}})) if (defined($opts{nofile}));
	push @{$opts{nofile}}, split(/ /, $nofiles);
	@{$opts{nodir}} = split(/,/, join(',', @{$opts{nodir}})) if (defined($opts{nodir}));
	push @{$opts{nodir}}, split(/ /, $nodirs);

	# setup executables

	$loopcmp = "/usr/bin/mksquashfs";
	$loopmod = "squashfs";

	# do other things before starting
	check_kernel();
	check_dietlibc();
	check_resolution();
	check_root();
	check_apps();

	# pretty up
	print "\n\n";
}


### check for the kernel version
sub check_kernel {
	# this should really be perl regex's

	$kernel26 = 1;
	$modfile = "/etc/modprobe.conf";
	$depmod = "/sbin/depmod";
	$modext = ".ko";
#	$modules = $modules_26;
#	$modules_opt = $modules_opt_26;	

	# check for identical kernels
	if ($o_kernel eq $o_mbkopt) {
	    print "\n";
	    print "\nThe LiveCD kernel and multi boot kernel are identical.\n";
    	    print "Please choose another kernel or use the --kernel option.\n";
    	    print "\n";
	    cleanup ();
    	    exit (1);
	}
}


### checks if this is a MDK dietlibc architecture
sub check_dietlibc {
	my $arch = get_exec("uname -m");
	$dietarch = 1 if ($arch =~ m/i.86|x86_64/);
}


### checks the resolution given
sub check_resolution {
	if ($o_resolution =~ m/^normal/) {
		$o_vgamode = "normal";
		$o_splash = "no";
	}
	elsif ($o_resolution =~ m/^640x480/) {
		$o_vgamode = 785;
	}
	elsif ($o_resolution =~ m/^800x600/) {
		$o_vgamode = 788;
	}
	elsif ($o_resolution =~ m/^1024x768/) {
		$o_vgamode = 791;
	}
	elsif ($o_resolution =~ m/^1280x1024/) {
		$o_vgamode = 794;
	}
	elsif ($o_resolution =~ m/^1600x1200/) {
		$o_vgamode = 797;
	}
	else {
		die_("\nFATAL: Invalid resolution '$o_resolution' specified with '--resolution' option
       Valid resolutions are:
             normal
            640x480
            800x600
           1024x768
          1280x1024
          1600x1200\n");
	}
}


### see if we are indeed root
sub check_root {
	die_("\nFATAL: You have to be root to execute this program\n") if ($> > 0);
}


### see if we are indeed root
sub check_apps {
	die_("\nFATAL: The '/usr/bin/genisoimage' program is not available, please install the correct package\n") unless (-f '/usr/bin/genisoimage');
	if (($o_bootloader =~m/^grub/) && ($o_stage2 eq '')) {
	    die("\nFATAL: The Grub program is not available, please install the correct package\n")
	}
	die_("\nFATAL: The '$loopcmp' program is not available, please install the correct package\n") unless (!defined($loopcmp) || -f $loopcmp);
}


### execute a command
sub do_cmd {
	my ($cmd, $prog) = @_;
	set_progress($prog-1, $cmd) if (defined($prog));
	system($cmd) and die_("\nFATAL: Execution of '$cmd' failed\n");
	set_progress($prog, $cmd) if (defined($prog));
}


### copy a file
sub do_copy {
	my ($src, $mode, $dest, $prog) = @_;
	my $cmd = ($mode > 0) ? "install -m $mode $src $dest" : "cp -aL $src $dest";
	set_progress($prog-1, $cmd) if (defined($prog));
	system($cmd);
	set_progress($prog, $cmd) if (defined($prog));
}   	


### create an initrd image
sub create_initrd {
	# get our modules
	$modules = $modules_26;
	$modules_opt = $modules_opt_26;
	my @allmods = split(/ /, $modules);
	push @allmods, $loopmod if (defined($loopmod));
	push @allmods, $o_ufs;
	my @allmods_opt = split(/ /, $modules_opt);

	# start progress bar
	my $pos = 0;
	if ($o_mbkopt ne "") {
	    start_progress("Creating $o_kernel initrd", 42+scalar(@allmods));
	  } else {
	    start_progress("Creating initrd", 42+scalar(@allmods));
	}

	# create directories
	my $dir = $workdir."/initrd.dir";
	mkdir_p("$dir/$_") foreach "lib/modules/$o_kernel/kernel", qw(bin dev etc/livecd/hwdetect etc/rc.d/ etc/profile.d proc ramfs sbin usr/bin usr/sbin modules /usr/lib /usr/share/ldetect-lst /lib/module-init-tools cdrom loopfs tmp ramfs);
	set_progress(++$pos, "mkdir -p $dir/...");

	# find our modprobe
	if (defined($dietarch)) {
		do_copy("$o_root/sbin/modprobe",	755,	"$dir/sbin/modprobe",     ++$pos)
	}

	# copy files
	do_copy($o_root."/usr/bin/busybox",               755, "$dir/bin/busybox",             ++$pos);
        #Bash required to handle function definitions in /etc/init.d/functions correctly which
        #is a dependency of start_udev.
        do_copy("/bin/bash",                      755, "$dir/bin/bash",                ++$pos);
	do_copy($o_root."/usr/lib/drakx-installer-binaries/probe-modules",            755, "$dir/bin/probe-modules",       ++$pos);
	do_copy($o_root."/usr/share/mklivecd/linuxrc",    755, "$dir/linuxrc",                 ++$pos);
	do_copy($o_root."/usr/share/mklivecd/rc.sysinit", 755, "$dir/etc/rc.d/rc.sysinit",     ++$pos);
        if ($o_ufs =~ m/^unionfs/) {
	    do_cmd("perl -pi -e 's/aufs/unionfs/' $dir/linuxrc");
	    do_cmd("perl -pi -e 's/AUFS/UNIONFS/' $dir/etc/rc.d/rc.sysinit");
	    do_cmd("perl -pi -e 's/aufs/unionfs/' $dir/etc/rc.d/rc.sysinit");
	    do_cmd("perl -pi -e 's/ro none/ro unionfs/' $dir/etc/rc.d/rc.sysinit");
	}
        do_copy($o_root."/usr/sbin/hwdetect",             755, "$dir/usr/sbin/hwdetect",       ++$pos);
	do_copy("/usr/share/mklivecd/halt.local", 755, "$dir/sbin/halt.local",         ++$pos);
	do_copy($o_root."etc/inittab",            644, "$dir/etc/inittab",             ++$pos);
	do_copy($o_root."sbin/init",              755, "$dir/sbin/init.dynamic",       ++$pos);

        do_cmd(" cp /lib/libtermcap* $dir/lib/.",                                      ++$pos);
        do_cmd(" cp /lib/libdl.* $dir/lib/.",                                          ++$pos);
        do_cmd(" cp /lib/libc.* $dir/lib/.",                                           ++$pos);
        do_cmd(" cp /lib/libc-* $dir/lib/.",                                           ++$pos);
        do_cmd(" cp -a /lib/udev $dir/lib/.",                                          ++$pos);
        do_cmd(" cp /lib/ld-linux.* $dir/lib/.",                                       ++$pos);
        do_cmd(" cp /lib/libext2fs.* $dir/lib/.",                                      ++$pos);
        do_cmd(" cp /lib/libncurses.* $dir/lib/.",                                     ++$pos);
        do_cmd(" cp /lib/libz.* $dir/lib/.",                                           ++$pos);
        do_cmd(" cp /lib/libmodprobe.* $dir/lib/.",                                    ++$pos);
        do_cmd(" cp /lib/libblkid.* $dir/lib/.",                                       ++$pos);
        do_cmd(" cp /lib/libuuid.* $dir/lib/.",                                        ++$pos);
	do_copy($o_root."lib/module-init-tools/ldetect-lst-modules.alias", 0,   "$dir/lib/module-init-tools",   ++$pos);
	do_copy($o_root."lib/modules/${o_kernel}/modules.dep",             0,   "$dir/modules",                 ++$pos);
	do_copy($o_root."lib/modules/${o_kernel}/modules.description",     0,   "$dir/modules",                 ++$pos);
	do_copy($o_root."lib/modules/${o_kernel}/modules.alias",           0,   "$dir/modules",                 ++$pos);
	do_copy($o_root."usr/share/pci.ids",                               0,   "$dir/usr/share",               ++$pos);
	do_copy($o_root."usr/share/ldetect-lst/fallback-modules.alias",    0,   "$dir/usr/share/ldetect-lst",   ++$pos);
	do_copy($o_root."usr/share/ldetect-lst/pcitable.gz",               644, "$dir/usr/share/ldetect-lst",   ++$pos);
	do_copy($o_root."usr/share/ldetect-lst/usbtable.gz",               644, "$dir/usr/share/ldetect-lst",   ++$pos);
	# Copy but hide the udev binaries
	do_cmd("mkdir $dir/sbin/tmp",			                                ++$pos);
        do_cmd("mkdir $dir/bin/tmp",                    ++$pos);
        #Use normal mknod since busybox version doesn't recognise permissions parameter
        do_copy($o_root."/bin/mknod",    755, "$dir/bin/tmp", 				++$pos);
	do_copy($o_root."/sbin/udev*",	          755, "$dir/sbin/tmp",			++$pos);
        do_copy($o_root."/sbin/start_udev",    755, "$dir/sbin/tmp",                    ++$pos);
        do_copy($o_root."/sbin/consoletype",    755, "$dir/sbin/tmp",                   ++$pos);
        do_copy($o_root."/sbin/fstab-decode",    755, "$dir/sbin/tmp",                  ++$pos);
        do_copy($o_root."/sbin/create_static_dev_nodes",    755, "$dir/sbin/tmp",                 ++$pos);
	do_cmd("cp -a $o_root/etc/udev $dir/etc",	                                ++$pos);
	if (-e "$o_root/etc/dev.d") {
	    do_cmd("cp -a $o_root/etc/dev.d $dir/etc",	++$pos);
	}
	do_cmd("cp -a $o_root/etc/sysconfig $dir/etc",	++$pos);

        do_cmd("mkdir -p $dir/etc/rc.d/init.d",  ++$pos);
        do_cmd("ln -s $o_root/etc/rc.d/init.d $dir/etc/init.d",  ++$pos);
        do_copy("$o_root/etc/rc.d/init.d/functions", 755, "$dir/etc/rc.d/init.d",  ++$pos);
	do_cmd("mknod $dir/dev/initrd b 1 250", ++$pos);
	do_cmd("mknod $dir/dev/console c 5 1",  ++$pos);
	do_cmd("mknod $dir/dev/null c 1 3",     ++$pos);
	do_cmd("mknod $dir/dev/systty c 4 0",   ++$pos);
	do_cmd("mknod $dir/dev/ram b 1 1",      ++$pos);
	do_cmd("mknod $dir/dev/tty$_ c 4 $_",   ++$pos) foreach((1,2,3,4));
	do_cmd("mknod $dir/dev/lvm b 109 0",    ++$pos);

	# copy modules
	foreach my $mod (@allmods) {
#		my $modpath = $o_root."lib/modules/$o_kernel/kernel/$mod$modext";
		my $modpath = qx(find ${o_root}lib/modules/${o_kernel} -name $mod$modext*);
		$modpath =~ s,.gz,,; chomp($modpath);  ## chop of the .gz
		$mod =~ s,.*/,, ; ## basename and perl (file::basename)
		my $tomod = "$dir/lib/modules/$o_kernel/kernel/$mod$modext";
		if (-f $modpath) {
			do_copy($modpath, 644, $tomod, ++$pos);
		}
		else {
			$modpath = $modpath.".gz";
			if (-f $modpath) {
				do_cmd("gzip -dc $modpath >$tomod", ++$pos);
			}
			else {
				my $modopt = undef;
				foreach my $opt (@allmods_opt) {
					$opt = "$opt$modext";
					$modopt = 1 if ($opt =~ m/$mod/);
				}
				set_progress(++$pos, $modpath);
			}
		}
	}

	# generate modules file
	do_cmd(":>$dir$modfile", ++$pos);
	my $cfg = defined($kernel26) ? "" : "--config $dir$modfile";
	do_cmd("(depmod -a --basedir $dir ".$cfg." $o_kernel || cp -f $o_root/lib/modules/$o_kernel/modules.dep $dir/lib/modules/$o_kernel) 2>/dev/null", ++$pos);
        do_cmd("(cp -f $dir/lib/modules/$o_kernel/modules.dep $dir/modules) 2>/dev/null",                 ++$pos);
	# get sizes$o_kernel
	my $size = get_exec("du -ks $dir | awk '{print \$1}'");
	$size = $size + 250;
	set_progress(++$pos, "du -ks $dir");

	# number of inodes
	my $inodes = 1250;
	my $num = get_exec("find $dir | wc -l");
	$inodes = $inodes + $num;
	$size = int($size + ($inodes / 10)) + 1; # 10 inodes needs 1K
	set_progress(++$pos, "find $dir | wc -l");

	# do magic
	my $initrd = "$workdir/livecd/isolinux/initrd";
	my $mnt = "$workdir/initrd.mnt";
	do_cmd("mkdir -p $workdir/livecd/isolinux",                         ++$pos);
	do_cmd("mkdir -p $workdir/livecd/boot/grub",                        ++$pos);
	do_cmd("dd if=/dev/zero of=$initrd bs=1k count=$size 2> /dev/null", ++$pos);
	do_cmd("mke2fs -q -m 0 -F -N $inodes -s 1 $initrd",                 ++$pos);
	do_cmd("mkdir -p $mnt ; mount -o loop -t ext2 $initrd $mnt",        ++$pos);
	do_cmd("rm -rf $mnt/lost+found",                                    ++$pos);
	do_cmd("(cd $dir ; tar cf - .) | (cd $mnt ; tar xf -)",             ++$pos);
	do_cmd("umount $mnt",                                               ++$pos);
	do_cmd("(cd $workdir/livecd/isolinux ; gzip -9 initrd)",            ++$pos);

	# make splash
	if (($o_splash !~ m/no/) && ($o_root =~ m/union/)) {
	if (-e "/tmp/bootsplash/scripts/make-boot-splash") {
	    do_cmd("rm -rf /tmp/bootsplash");
        }
	do_cmd("mkdir -p /tmp/bootsplash");
	do_cmd("cp -r /usr/share/bootsplash/scripts /tmp/bootsplash");
	do_cmd("perl -pi -e 's#usr/share/bootsplash#tmp/bootsplash#' /tmp/bootsplash/scripts/make-boot-splash");
	do_cmd("perl -pi -e 's/^warn/#warn/' /tmp/bootsplash/scripts/remove-boot-splash");

	do_cmd("mv -f $initrd.gz /tmp ; \
		/tmp/bootsplash/scripts/make-boot-splash /tmp/initrd.gz $o_resolution; \
		mv -f /tmp/initrd.gz $workdir/livecd/isolinux", ++$pos);
	}
	else {
		if ($o_splash !~ m/no/) {  
		if (-e "/tmp/bootsplash/scripts/make-boot-splash") {
		    do_cmd("rm -rf /tmp/bootsplash");
    		}
		do_cmd("mkdir -p /tmp/bootsplash");
		do_cmd("cp -r /usr/share/bootsplash/scripts /tmp/bootsplash");
		do_cmd("perl -pi -e 's#usr/share/bootsplash#tmp/bootsplash#' /tmp/bootsplash/scripts/make-boot-splash");
		do_cmd("perl -pi -e 's/^warn/#warn/' /tmp/bootsplash/scripts/remove-boot-splash");		
		do_cmd("[ -d $o_root/tmp ] || mkdir $o_root/tmp ; \
		mv -f $initrd.gz $o_root/tmp ; \
		/tmp/bootsplash/scripts/make-boot-splash $o_root/tmp/initrd.gz $o_resolution; \
		mv -f $o_root/tmp/initrd.gz $workdir/livecd/isolinux", ++$pos);
		}
	}
	
  	# we are done
	end_progress ();
}


## check for legacy kernel and create initrd2
sub create_initrd2 {
    if  ($o_mbkopt ne "" && -d "/lib/modules/$o_mbkopt") {	
	# get our modules
	$modules = $modules_26;
	$modules_opt = $modules_opt_26;	
	my @allmods = split(/ /, $modules);
	push @allmods, $loopmod if (defined($loopmod));
	push @allmods, $o_ufs;
	my @allmods_opt = split(/ /, $modules_opt);

	# start progress bar
	my $pos = 0;
	start_progress("Creating $o_mbkopt initrd", 42+scalar(@allmods));

	# create directories
	my $dir = $workdir."/initrd2.dir";
	mkdir_p("$dir/$_") foreach "lib/modules/$o_mbkopt/kernel", qw(bin dev etc/livecd/hwdetect etc/rc.d/ proc ramfs sbin usr/bin usr/sbin cdrom loopfs ramfs);
	set_progress(++$pos, "mkdir -p $dir/...");

	# find our modprobe
	if (defined($dietarch)) {
		do_copy("$o_root/sbin/modprobe",	755,	"$dir/sbin/modprobe",     ++$pos)
	}

	# copy files
	do_copy("/usr/bin/busybox",               755, "$dir/bin/busybox",             ++$pos);
	do_copy("/usr/share/mklivecd/linuxrc",    755, "$dir/linuxrc",                 ++$pos);
	do_copy("/usr/share/mklivecd/rc.sysinit", 755, "$dir/etc/rc.d/rc.sysinit",     ++$pos);
        if ($o_ufs =~ m/^unionfs/) {
	    do_cmd("perl -pi -e 's/aufs/unionfs/' $dir/linuxrc");
	    do_cmd("perl -pi -e 's/AUFS/UNIONFS/' $dir/etc/rc.d/rc.sysinit");
	    do_cmd("perl -pi -e 's/aufs/unionfs/' $dir/etc/rc.d/rc.sysinit");
	    do_cmd("perl -pi -e 's/ro none/ro unionfs/' $dir/etc/rc.d/rc.sysinit");
	}
	do_copy("/usr/sbin/hwdetect",             755, "$dir/usr/sbin/hwdetect",       ++$pos);
	do_copy("/usr/share/mklivecd/halt.local", 755, "$dir/sbin/halt.local",         ++$pos);
        do_copy($o_root."/etc/inittab",            644, "$dir/etc/inittab",            ++$pos);
        do_copy($o_root."/sbin/init",              755, "$dir/sbin/init.dynamic",      ++$pos);
        do_copy($o_root."/lib/libc.so.6",            0, "$dir/lib",                    ++$pos);
        do_copy($o_root."/lib/ld-linux.so.2",        0, "$dir/lib",                    ++$pos);


	# Copy but hide the udev binaries
	do_cmd("mkdir $dir/sbin/tmp",			++$pos);
	do_copy($o_root."/sbin/udev*",	  755, "$dir/sbin/tmp",			++$pos);
	do_cmd("cp -a $o_root/etc/udev $dir/etc",	++$pos);
	if (-e "$o_root/etc/dev.d") {
	    do_cmd("cp -a $o_root/etc/dev.d $dir/etc",	++$pos);
	}
	do_cmd("cp -a $o_root/etc/hotplug $dir/etc",	++$pos);
	do_cmd("cp -a $o_root/etc/sysconfig $dir/etc",	++$pos);

	do_cmd("mknod $dir/dev/initrd b 1 250", ++$pos);
	do_cmd("mknod $dir/dev/console c 5 1",  ++$pos);
	do_cmd("mknod $dir/dev/null c 1 3",     ++$pos);
	do_cmd("mknod $dir/dev/systty c 4 0",   ++$pos);
	do_cmd("mknod $dir/dev/ram b 1 1",      ++$pos);
	do_cmd("mknod $dir/dev/tty$_ c 4 $_",   ++$pos) foreach((1,2,3,4));
	do_cmd("mknod $dir/dev/lvm b 109 0",    ++$pos);

	# copy modules
	foreach my $mod (@allmods) {
#		my $modpath = $o_root."lib/modules/$o_mbkopt/kernel/$mod$modext";
		my $modpath = qx(find ${o_root}lib/modules/${o_mbkopt} -name $mod$modext*);
		$modpath =~ s,.gz,,; chomp($modpath);  ## chop of the .gz
		$mod =~ s,.*/,, ; ## basename and perl (file::basename)
		my $tomod = "$dir/lib/modules/$o_mbkopt/kernel/$mod$modext";
		if (-f $modpath) {
			do_copy($modpath, 644, $tomod, ++$pos);
		}
		else {
			$modpath = $modpath.".gz";
			if (-f $modpath) {
				do_cmd("gzip -dc $modpath >$tomod", ++$pos);
			}
			else {
				my $modopt = undef;
				foreach my $opt (@allmods_opt) {
					$opt = "$opt$modext";
					$modopt = 1 if ($opt =~ m/$mod/);
				}
				set_progress(++$pos, $modpath);
			}
		}
	}

	# generate modules file
	do_cmd(":>$dir$modfile", ++$pos);
	my $cfg = defined($kernel26) ? "" : "--config $dir$modfile";
	do_cmd("(depmod -a --basedir $dir ".$cfg." $o_mbkopt || cp -f $o_root/lib/modules/$o_mbkopt/modules.dep $dir/lib/modules/$o_mbkopt) 2>/dev/null", ++$pos);

	# get sizes$o_kernel
	my $size = get_exec("du -ks $dir | awk '{print \$1}'");
	$size = $size + 250;
	set_progress(++$pos, "du -ks $dir");

	# number of inodes
	my $inodes = 1250;
	my $num = get_exec("find $dir | wc -l");
	$inodes = $inodes + $num;
	$size = int($size + ($inodes / 10)) + 1; # 10 inodes needs 1K
	set_progress(++$pos, "find $dir | wc -l");

	# do magic
	my $initrd2 = "$workdir/livecd/isolinux/initrd2";
	my $mntlg = "$workdir/initrd2.mnt";
	do_cmd("mkdir -p $workdir/livecd/isolinux",                         ++$pos);
	do_cmd("mkdir -p $workdir/livecd/boot/grub",                        ++$pos);
	do_cmd("dd if=/dev/zero of=$initrd2 bs=1k count=$size 2> /dev/null", ++$pos);
	do_cmd("mke2fs -q -m 0 -F -N $inodes -s 1 $initrd2",                 ++$pos);
	do_cmd("mkdir -p $mntlg ; mount -o loop -t ext2 $initrd2 $mntlg",    ++$pos);
	do_cmd("rm -rf $mntlg/lost+found",                                    ++$pos);
	do_cmd("(cd $dir ; tar cf - .) | (cd $mntlg ; tar xf -)",             ++$pos);
	do_cmd("umount $mntlg",                                               ++$pos);
	do_cmd("(cd $workdir/livecd/isolinux ; gzip -9 initrd2)",            ++$pos);

	# make splash
	if ($o_splash !~ m/no/) {
	if (-e "/tmp/bootsplash/scripts/make-boot-splash") {
	    do_cmd("rm -rf /tmp/bootsplash");
        }
	do_cmd("mkdir -p /tmp/bootsplash");
	do_cmd("cp -r /usr/share/bootsplash/scripts /tmp/bootsplash");
	do_cmd("perl -pi -e 's#usr/share/bootsplash#tmp/bootsplash#' /tmp/bootsplash/scripts/make-boot-splash");
	do_cmd("perl -pi -e 's/^warn/#warn/' /tmp/bootsplash/scripts/remove-boot-splash");
	
	do_cmd("[ -d $o_root/tmp ] || mkdir $o_root/tmp ; \
		mv -f $initrd2.gz $o_root/tmp ; \
		/tmp/bootsplash/scripts/make-boot-splash $o_root/tmp/initrd2.gz $o_resolution; \
		mv -f $o_root/tmp/initrd2.gz $workdir/livecd/isolinux", ++$pos);
	    }	
    } else {
	    print "\n";
	    print "The option --mbkopt $o_mbkopt was used.\n";
	    print "Is kernel $o_mbkopt installed?\n";
	    print "\n";
	    cleanup ();
	    exit (1);
	}         

  	# we are done
	end_progress();
}


### create the compressed image
sub create_compressed {
	if (defined($opts{img}) && (-f $opts{img})) {
		do_cmd("ln $opts{img} $workdir/livecd/livecd.$o_looptype");
	}
	else {
		my $pos = 0;
		start_progress("Setting filesystem parameters", 5);

		# handle excludes
		do_cmd(":>$workdir/excludes.list", ++$pos);
		if (defined($opts{nodir})) {
			my $ex = join("\n", @{$opts{nodir}});
			do_cmd("(find $o_root -type d 2>/dev/null | sed -e 's,^$o_root,/,g' | grep '$ex' | sed 's,^/,$o_root,' >>$workdir/excludes.list)", ++$pos);

		}
		if (defined($opts{nofile})) {
			my $ex = join("\n", @{$opts{nofile}});
			do_cmd("(find $o_root -type f 2>/dev/null | sed -e 's,^$o_root,/,g' | grep '$ex' | sed 's,^/,$o_root,' >>$workdir/excludes.list)", ++$pos);
		}

		# handle sort
		do_cmd(":>$workdir/sort.list", ++$pos);
		do_cmd("cat ".$opts{sort}." >>$workdir/sort.list", ++$pos) if (defined($opts{sort}));
		end_progress();

		$pos = 0;
		if ($o_looptype =~ m/sqfs/) {
			my @files = qx(find $o_root -type f 2>/dev/null);
			my $total = scalar(@files);
			start_progress("Creating compressed image", $total+2);
			my $iso = "$loopcmp $o_root $workdir/livecd/livecd.$o_looptype -info -ef $workdir/excludes.list";
			$iso = "$iso -sort $workdir/sort.list" if (defined($opts{sort}));
			$iso = "$iso 2>&1";
			print "DEBUG: $iso\n" if (defined($opts{debug}));
			open CMP, "$iso |" or die_("\nFATAL: Unable to execute '$iso'\n");
			my $line;
			my $linep = "";
			while ($line = <CMP>) {
				if ($line =~ m/mksquashfs: file/) {
					$linep = $line;
					$pos++;
					set_progress($pos, $linep);
				}
				else {
					if (defined($opts{debug})) {
						print "$line\n" if ($line =~ m/Error/);
					}
				}
				set_progress($pos, $linep);
			}
			close CMP;
			do_cmd("chmod 644 $workdir/livecd/livecd.$o_looptype", $total+1); # Failure = out of space
			do_cmd("ln $workdir/livecd/livecd.$o_looptype $opts{img}", $total+2) if (defined($opts{img}));
			end_progress();
		}
		else {
			start_progress("Creating loop image", 10001);
			my $iso = "genisoimage $genisoimage_opts -R -exclude-list $workdir/excludes.list -hide-rr-moved -cache-inodes -no-bak -pad -v -v";
			$iso = "$iso -sort $workdir/sort.list" if (defined($opts{sort}));


			$iso = "$iso -o $workdir/livecd/livecd.$o_looptype $o_root";

			$iso = "($iso) 2>&1";
			print "DEBUG: $iso\n" if (defined($opts{debug}));
			open CMP, "$iso |" or die_("\nFATAL: Unable to execute '$iso'\n");
			my $line;
			while ($line = <CMP>) {
				if ($line =~ m/done, estimate/) {
					$line =~ s/^ //g while ($line =~ m/^ /);
					my ($per, @rest) = split(/ /, $line);
					$per =~ s/%//;
					$pos = int($per*100);
				}
				set_progress($pos, $line);
			}
			close CMP;
			do_cmd("ln $workdir/livecd/livecd.$o_looptype $opts{img}", 10001) if (defined($opts{img}));
			end_progress();
		}
	}
}


### create the isolinux stuff
sub create_isolinux {
	my $pos = 0;
	start_progress("Creating isolinux boot", 9);

	# copy boot images
	my $bin = "/usr/lib/syslinux/isolinux.bin";
	die_("\nFATAL: '$bin' does not exist on your machine. You need to install the syslinux package.\n") unless (-f $bin);
	do_copy($bin, 644, "$workdir/livecd/isolinux/isolinux.bin", ++$pos);
	die_("\nFATAL: The kernel '".$o_root."boot/vmlinuz-$o_kernel' does not exist on your machine.\n") unless (-f $o_root."boot/vmlinuz-$o_kernel");
	do_copy($o_root."boot/vmlinuz-$o_kernel", 644, "$workdir/livecd/isolinux/vmlinuz", ++$pos);
	if ($o_mbkopt ne "") {
	    do_copy($o_root."boot/vmlinuz-$o_mbkopt", 644, "$workdir/livecd/isolinux/vmlinuz2", ++$pos)
        }
	do_copy("/usr/bin/mediacheck", 755, "$workdir/livecd/isolinux/mediacheck", ++$pos);
        do_copy($o_root."boot/memtest*.bin", 644, "$workdir/livecd/isolinux/memtest", ++$pos);
        if ($o_bootloader =~ m/^iso/) {
	    do_copy($o_root."usr/share/gfxboot/themes/$opts{boottheme}/install/*", 644, "$workdir/livecd/isolinux", ++$pos);
	  } else {
	    do_copy($o_stage2, 644, "$workdir/livecd/boot/grub/stage2_eltorito", ++$pos);
	    do_copy($o_root."boot/grub/*.xpm.gz", 644, "$workdir/livecd/boot/grub/livecd.xpm.gz", ++$pos);
	    do_copy($o_root."usr/share/gfxboot/themes/$opts{boottheme}/boot/*", 644, "$workdir/livecd/boot/grub", ++$pos);
        }

	# copy messages
	do_copy($opts{bootmsg}, 644, "$workdir/livecd/isolinux/livecd.msg", ++$pos) if (defined($opts{bootmsg}));
	if (defined($opts{bootimg})) {
		do_cmd("echo -n '' >$workdir/livecd/isolinux/livecd.msg", ++$pos);
		do_cmd("echo -e '\\030livecd.lss' >>$workdir/livecd/isolinux/livecd.msg", ++$pos);
		do_copy($opts{bootimg}, 644, "$workdir/livecd/isolinux/livecd.lss", ++$pos);
	}

	# write config
	my $appopt;
	if ($kernel26 == 1) {
		$appopt = "initrd=initrd.gz root=/dev/rd/3 acpi=on vga=$o_vgamode keyb=$o_keyboard";
	} else {
		$appopt = "initrd=initrd.gz root=/dev/rd/3 devfs=mount vga=$o_vgamode keyb=$o_keyboard";
	}
	$appopt .= " splash=$o_splash" if ($o_splash !~ m/no/);
        $appopt = "$appopt fastboot=yes automatic=method:cdrom ramdisk_size=64000" if (!defined($kernel26));
	$appopt = "$appopt fstab=".$opts{fstab} if (defined($opts{fstab}));
	$appopt = "$appopt home=usb" if (defined($opts{usbhome}));
	$appopt = "$appopt $_" foreach (@{$opts{bootopt}});

	open LANG, '>', "$workdir/livecd/isolinux/lang";
	print LANG $opts{bootlang}."\n";
	close LANG;
	
	if (defined($opts{bootmenu}) && -f $opts{bootmenu} )
	{
	    do_copy($opts{bootmenu}, 644, "$workdir/livecd/isolinux", ++$pos);
	} else {
	open CFG, '>', "$workdir/livecd/isolinux/isolinux.cfg";
	print CFG "default livecd\n";
	print CFG "prompt  ".(defined($opts{noprompt}) ? "0" : "1")."\n";
	print CFG "timeout $o_timeout\n";
	print CFG "display livecd.msg\n" if (-f "$workdir/livecd/isolinux/livecd.msg");
	foreach my $k (keys %{$opts{bootkey}}) {
		do_copy(${$opts{bootkey}}{$k}, 644, "$workdir/livecd/isolinux/livecd_$k.msg");
		print CFG "$k livecd_$k.msg\n";
	}
	print CFG "gfxboot bootlogo\n";
        print CFG "label livecd\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd $appopt\n";
	print CFG "label VideoSafeModeFBDev\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd $appopt framebuffer\n";
	print CFG "label VideoSafeModeVesa\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd $appopt vesa\n";
	print CFG "label Safeboot\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd initrd=initrd.gz root=/dev/rd/3 acpi=off vga=normal keyb=us noapic nolapic noscsi nopcmcia\n";
	print CFG "label Console\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd 3 $appopt\n";
	print CFG "label Copy2ram\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd copy2ram $appopt splash=verbose\n";
	print CFG "label MediaCheck\n";
	print CFG "    kernel vmlinuz\n";
	print CFG "    append livecd=livecd md5sum $appopt splash=verbose\n";
	print CFG "label Memtest\n";
	print CFG "    kernel memtest\n";	
	print CFG "#label LegacyKernel\n";
	print CFG "#    kernel vmlinuz2\n";
	print CFG "#    append livecd=livecd initrd=initrd2.gz $appopt\n";
	close CFG;
	}

    if ($o_bootloader =~ m/^grub/) {
	if (defined($opts{bootmenu}) && -f $opts{bootmenu} )
	{
	    do_copy($opts{bootmenu}, 644, "$workdir/livecd/boot/grub", ++$pos);
	} else {
	open CFG, '>', "$workdir/livecd/boot/grub/menu.lst";
	print CFG "default 0\n";
	print CFG "timeout 30\n";
	print CFG "gfxmenu (cd)/boot/grub/message\n\n";
	print CFG "title LiveCD\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd $appopt\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title VideoSafeModeFBDev\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd $appopt framebuffer\n";
	print CFG "title VideoSafeModeVesa\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd $appopt vesa\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title Safeboot\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd root=/dev/rd/3 acpi=off vga=normal keyb=us noapic nolapic noscsi nopcmcia\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title Console\n";
	print CFG "kernel (cd)/isolinux/vmlinuz $appopt 3\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title Copy2ram\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd copy2ram $appopt splash=verbose\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title MediaCheck\n";
	print CFG "kernel (cd)/isolinux/vmlinuz livecd=livecd md5sum $appopt splash=verbose\n";
	print CFG "initrd (cd)/isolinux/initrd.gz\n\n";
	print CFG "title Memtest\n";
	print CFG "kernel (cd)/isolinux/memtest\n\n\n";
	print CFG "#title LegacyKernel\n";
	print CFG "#kernel (cd)/isolinux/vmlinuz2 livecd=livecd initrd=initrd2.gz $appopt\n";
	print CFG "#initrd (cd)/isolinux/initrd2.gz\n\n";
	close CFG;
	}
    }

	set_progress(++$pos, "$workdir/livecd/isolinux/isolinux.cfg");

	if ($o_mbkopt ne "") {
		if ($o_bootloader =~ m/^iso/) {
		    do_cmd("perl -pi -e 's/initrd2.gz initrd=initrd.gz/initrd2.gz/' $workdir/livecd/isolinux/isolinux.cfg");
	    	    do_cmd("perl -pi -e 's/#//' $workdir/livecd/isolinux/isolinux.cfg");
		    if ($o_mbkopt !~ m/lgc/) {	
			do_cmd("perl -pi -e 's/LiveCD/LiveCD-".$o_kernel."/' $workdir/livecd/isolinux/isolinux.cfg");
		        do_cmd("perl -pi -e 's/LegacyKernel/Kernel-".$o_mbkopt."/' $workdir/livecd/isolinux/isolinux.cfg");
		    }
    	        } else {
		    do_cmd("perl -pi -e 's/initrd2.gz initrd=initrd.gz/initrd2.gz/' $workdir/livecd/boot/grub/menu.lst");
		    do_cmd("perl -pi -e 's/#//' $workdir/livecd/boot/grub/menu.lst");
			if ($o_mbkopt !~ m/lgc/) {
		        do_cmd("perl -pi -e 's/LiveCD/LiveCD-".$o_kernel."/' $workdir/livecd/boot/grub/menu.lst");
			    do_cmd("perl -pi -e 's/LegacyKernel/Kernel-".$o_mbkopt."/' $workdir/livecd/boot/grub/menu.lst");
		        }
		}
        }		
	# create boot catalogue
	do_cmd("dd if=/dev/zero of=$workdir/livecd/isolinux/boot.cat bs=1k count=2 2> /dev/null", ++$pos);

	end_progress();
}


### create the final iso
sub create_finaliso {
	# create a sort-file
	my $pos = 0;
	start_progress("Creating final iso", 10001);
	open SORT, '>', "$workdir/sort_iso.list";
	print SORT "$workdir/livecd/isolinux/isolinux.bin 500\n";
	print SORT "$workdir/livecd/isolinux/isolinux.cfg 499\n";
	print SORT "$workdir/livecd/isolinux/vmlinuz 498\n";
	print SORT "$workdir/livecd/isolinux/initrd.gz 497\n";
	print SORT "$workdir/livecd/isolinux/* 450\n";
	print SORT "$workdir/livecd/livecd.$o_looptype 400\n";
	close SORT;
	set_progress(++$pos, "$workdir/sort_iso.list");

	# create actual iso
	$opts{isoextrafiles} = "" unless (defined($opts{isoextrafiles}));
	if ($o_bootloader =~ m/^grub/) {
		$loader = "boot/grub/stage2_eltorito";
	}
	elsif ($o_bootloader =~ m/^iso/) {
		$loader = "isolinux/isolinux.bin";
	}
	my $cmd="genisoimage -pad -l -R -J -v -v \\\
                  -V '".$opts{volumeid}."' \\\
                  -A '".$opts{application}."' \\\
                  -p '".$opts{preparer}."' \\\
                  -P '".$opts{publisher}."' \\\
                  -b $loader -c isolinux/boot.cat -hide-rr-moved -no-emul-boot -boot-load-size 4 -boot-info-table -sort $workdir/sort_iso.list -o $finaliso $workdir/livecd/ ".$opts{isoextrafiles}." 2>&1";
          open ISO, "$cmd |" or die_("\nFATAL: Unable to execute '$cmd'\n");
          my $line;
          while ($line = <ISO>) {
		if ($line =~ m/done, estimate/) {
			$line =~ s/^ //g while ($line =~ m/^ /);
			my ($per, @rest) = split(/ /, $line);
			$per =~ s/%//;
			$pos = int($per*100)+1;
		}
		set_progress($pos, $line);
	}
	close ISO;
	end_progress();
}


### create the embedded md5sum
sub create_md5 {
	my $isosize = get_exec("ls -al --block-size=1M $finaliso | awk '{print \$5 }'");
	start_progress("Embedding MD5 checksum", $isosize);
	open MD5, "implantisomd5 $finaliso |" or die_("\nFATAL: Unable to execute 'implantisomd5'\n");
	my $line;
	my $pos = 0;
	while ($line = <MD5>) {
		chomp($line);
		if ($line =~ /^Read/) {
			$pos = $line;
			$pos =~ s/Read//;
			$pos =~ s/MB//;
			$pos =~ s/\s// while ($pos =~ /\s/);
		}
		set_progress($pos, $line);
	}
	close MD5;
	end_progress();
}


### clean everything
sub cleanup {
	if (defined($workdir)) {
		system("umount $workdir/initrd.mnt 2>/dev/null");
		system("rm -rf $workdir") unless (defined($opts{noclean}));
	}
	    if (-e "/tmp/bootsplash/scripts/make-boot-splash") {
		do_cmd("rm -rf /tmp/bootsplash");
	    }
}


### signals
sub die_ {
	die('DIE', join(' ', @_));
}
sub do_signal {
	my ($signal, $text) = @_;
	end_thread();
	my $to = $text ? $text : "\nFATAL: Interrupted.\n";
	chomp($to);
	print pack("A80", $to)."\n";
	cleanup();
	exit(1);
}


### main program entry point
MAIN: {
	$SIG{INT} = \&do_signal;
	$SIG{KILL} = \&do_signal;
	$SIG{PIPE} = \&do_signal;

	print_banner();
	parse_options();

	create_initrd();
	create_initrd2() if ($o_mbkopt ne "");;

	create_compressed();
	create_isolinux();
	create_finaliso();
	create_md5() if (defined($opts{md5sum}));;
	cleanup();

	my $finalsize = get_exec("ls -al $finaliso | awk '{print \$5 }'");
	print "\nCreated '$finaliso' (".fmt_number($finalsize)." bytes) in ".fmt_elapsed(time-$starttime)."\n\n";
}
