package MDV::Draklive::Initrd; use MDK::Common; use common; use list_modules; use MDV::Draklive::Utils; use MDV::Draklive::StorageFS; use MDV::Draklive::Loopback; use MDV::Draklive::Overlay; my %module_to_config = ( ext3 => 'EXT3_FS', sd_mod => 'BLK_DEV_SD', ); sub nls_modules { my ($live) = @_; my $fs = $live->{media}->get_media_setting('fs'); if_($fs eq 'vfat', 'nls_cp437'), #- default FAT codepage if_($fs !~ /^ext/, map { "nls_$_" } (map { "iso8859_$_" } 1..7, 9, 13..15), 'utf8'); } sub create_media_initrd { my ($live) = @_; create_initrd_for_media($live, $live->{media}); cp_f($live->get_builddir . $live->{prefix}{build}{boot} . $live->{media}->get_initrd_path, $live->{copy_initrd}) if $live->{copy_initrd}; } sub inst_initrd_file { my ($root, $initrd_tree, $file) = @_; if ($file =~ m|/s?bin/|) { inst_initrd_bin($root, $initrd_tree, $file); } else { mkdir_p($initrd_tree . dirname($file)); inst_initrd_dso_deps($root, $initrd_tree, $file) if $file =~ m|/lib[^/]*/|; cp_f($root . $file, $initrd_tree . $file) or die "unable to copy $file from system chroot\n"; } } sub inst_initrd_dso_deps { my ($root, $initrd_tree, $dso) = @_; foreach my $file (`chroot $root ldd $dso | awk '/\\// {if(\$2 == "=>") {print \$3} else {print \$1}}'`) { chomp $file; $file =~ s!^(/lib[^/]*)/(?:i686|tls)!$1!; my ($lib_prefix, $filename) = $file =~ m|(/lib[^/]*).*/([^/]+)$| or next; my $dest = $initrd_tree . $lib_prefix . '/' . $filename; mkdir_p($initrd_tree . $lib_prefix); -f $dest || cp_f($root . $file, $dest) or die "unable to copy $filename from system chroot\n"; } } sub inst_initrd_bin { my ($root, $initrd_tree, $bin) = @_; cp_f($root . $bin, $initrd_tree . '/bin/'); inst_initrd_dso_deps($root, $initrd_tree, $bin); } sub create_initrd_for_media { my ($live, $media) = @_; my $initrd = $live->get_builddir . $live->{prefix}{build}{boot} . $media->get_initrd_path; create_media_specific_initrd($live, $media, $live->get_system_root, $initrd, $live->find_kernel); } sub create_media_specific_initrd { my ($live, $media, $root, $initrd, $kernel) = @_; my $lib_prefix = find { glob($root . $_ . '/libc.so.*') } qw(/lib64 /lib); $lib_prefix or die 'unable to find system libraries in /lib or /lib64'; my $initrd_tree = $live->get_builddir . $live->{prefix}{build}{initrd} . '/' . $media->{storage}; rm_rf($initrd_tree) if -e $initrd_tree; mkdir_p($initrd_tree . $_) foreach qw(/bin /dev /proc /sys /tmp), map { $live->{prefix}{live}{mnt} . $_ } $live->{prefix}{media}{mnt}, $live->{mount}{root}, map { $_->{mountpoint} } grep { !$loop_types{$_->{type}}{skip_mkdir} } @{$live->{mount}{dirs} || []}; #- use nash with label support inst_initrd_bin($root, $initrd_tree, '/sbin/nash'); inst_initrd_bin($root, $initrd_tree, '/usr' . $lib_prefix . '/drakx-installer-binaries/probe-modules'); inst_initrd_bin($root, $initrd_tree, '/sbin/blockdev') if $media->get_media_setting('rereadpt'); inst_initrd_bin($root, $initrd_tree, '/usr/bin/strace') if $live->{debug}; foreach (chomp_(run_program::rooted_get_stdout($root, "/usr/sbin/splashy_find_files"))) { inst_initrd_file($root, $initrd_tree, $_); } #- busybox is required to: #- detect usb-storage process (we need sh/while/ps/grep) #- mount loopbacks read-only with losetup (useful over NFS) my $busybox = '/usr/bin/busybox'; inst_initrd_bin($root, $initrd_tree, $busybox); my $busybox_rooted = $root . $busybox; my @l = map { /functions:/ .. /^$/ ? do { s/\s//g; split /,/ } : () } `$busybox_rooted`; shift @l; symlink('busybox', $initrd_tree . "/bin/$_") foreach @l; my $fs = $media->get_media_setting('fs'); my @used_loop_types = uniq(map { $_->{type} } @{$live->{mount}{dirs}}); inst_initrd_bin($root, $initrd_tree, $_) foreach MDV::Draklive::StorageFS::get_files($fs), (map { @{$loop_types{$_} && $loop_types{$_}{files} || []} } @used_loop_types); output_p($initrd_tree . '/etc/fstab', ''); output_p($initrd_tree . '/etc/mtab', ''); my $loop_nb = 254; my $rrpt_dev = $media->get_media_setting('rereadpt'); require devices; devices::make($initrd_tree . "/dev/$_") foreach if_($rrpt_dev, $rrpt_dev), qw(console initrd null ram systty tty), (map { "tty$_" } 0..8), (map { "loop$_" } 0 .. $loop_nb); syscall_('mknod', $initrd_tree . "/dev/fb0", c::S_IFCHR(), makedev(29, 0)) or die "mknod failed (dev $_): $!"; #- pre-create devfsd compatibility loop devices (since busybox is still built with devfsd support) mkdir_p($initrd_tree . "/dev/loop"); cp_af($initrd_tree . "/dev/loop$_", $initrd_tree . "/dev/loop/$_") foreach 0 .. $loop_nb; print "using kernel $kernel\n"; my $kernel_root = "/lib/modules/" . $kernel; list_modules::load_dependencies($kernel_root . "/modules.dep", $root); my ($storage_modules, $skipped) = partition { list_modules::modname2filename($_) } uniq(map { modules::cond_mapping_24_26($_) } category2modules($media->get_media_setting('modules'))); my ($extra_modules, $extra_missing) = partition { list_modules::modname2filename($_) } category2modules($media->get_media_setting('media_modules')), nls_modules($live), $media->get_media_fs_module, @{$media->get_media_setting('extra_modules') || []}, (map { @{$loop_types{$_}{modules} || []} } uniq(map { $_->{type} } @{$live->{mount}{dirs} || []})), ($live->{mount}{overlay} ? @{$MDV::Draklive::Overlay::overlay{$live->{mount}{overlay}}{modules} || []} : ()); my @additional_modules = map { if_(m!([^/]+)\.ko(?:\.gz)?!, list_modules::filename2modname($1)) } @{$live->{system}{additional_modules}}; @$extra_modules = difference2($extra_modules, \@additional_modules); if (@{$live->{system}{exclude_modules} || []}) { print STDERR "excluding modules: " . join(' ', @{$live->{system}{exclude_modules}}) . "\n"; @$_ = difference2($_, $live->{system}{exclude_modules}) foreach $storage_modules, $extra_modules, $extra_missing; } my @missing = sort(difference2($extra_missing, \@additional_modules)); if (@missing) { my @config = map { if_(/^CONFIG_(.*)=y$/, $1) } cat_($root . "/boot/config-" . $kernel); my @really_missing = grep { !member($module_to_config{$_}, @config) } @missing; @really_missing and die "missing mandatory modules:\n" . join("\n", @really_missing, ''); } mkdir_p($initrd_tree . $kernel_root . "/kernel"); my @modules = (@$storage_modules, @$extra_modules); my @modules_closure = uniq(map { list_modules::dependencies_closure($_) } @modules); foreach my $m (@modules_closure) { my $full = list_modules::modname2path($m); mkdir_p(dirname($initrd_tree . $full)); cp_f($root . $full, $initrd_tree . $full); } foreach my $f (@{$live->{system}{additional_modules}}) { my $destdir = $initrd_tree . $kernel_root . "/kernel"; if ($f =~ /.gz$/) { cp_f($live->{settings}{config_root} . '/' . $f, $destdir); } else { my $m = basename($f); run_program::run('gzip', '>', "$destdir/$m.gz", '-c', $live->{settings}{config_root} . '/' . $f); } } run_('depmod', '-b', $initrd_tree, $kernel); mkdir_p($initrd_tree . "/etc/blkid"); #- for nash and showlabels cache mkdir_p($initrd_tree . "/lib/module-init-tools"); cp_f($root . "/lib/module-init-tools/ldetect-lst-modules.alias", $initrd_tree . "/lib/module-init-tools"); mkdir_p($initrd_tree . "/usr/share/ldetect-lst"); cp_f($root . "/usr/share/pci.ids", $initrd_tree . "/usr/share"); cp_f($root . "/usr/share/ldetect-lst/" . $_, $initrd_tree . "/usr/share/ldetect-lst") foreach qw(fallback-modules.alias pcitable.gz usbtable.gz); @$skipped and print STDERR "skipped modules: " . join(' ', sort(@$skipped)) . "\n"; my @extra_modules_closure = map { list_modules::modname2filename($_) } uniq(map { list_modules::dependencies_closure($_) } @$extra_modules); create_initrd_scriptlet($live, $media, @extra_modules_closure, @additional_modules); compress_initrd_tree($live, $initrd_tree, $initrd); add_splash($live, $initrd); } sub create_initrd_scriptlet { my ($live, $media, @modules) = @_; my $target = $live->{prefix}{live}{mnt} . ($live->{mount}{root} || $live->{prefix}{media}{mnt}); my $pre = $media->get_media_setting('pre'); my $fs = $media->get_media_setting('fs'); my $rrpt_dev = $media->get_media_setting('rereadpt'); my $debug_shell = "sh -c 'if grep -q initrd_debug /proc/cmdline; then exec sh /dev/console 2>/dev/console; fi'"; my ($mount_first, $mount_last) = partition { !$loop_types{$_->{type}}{delay_mount} } grep { exists $loop_types{$_->{type}}{mount} } @{$live->{mount}{dirs} || []}; my $initrdroot = "/initrd"; output_with_perm($live->get_builddir . $live->{prefix}{build}{initrd} . '/' . $media->{storage} . '/linuxrc', 0755, join("\n", "#!/bin/nash", #- required for labels and ps "nash-mount -t proc /proc /proc", #- required for cdrom labels "nash-mount -t sysfs /sys /sys", "splashy_chvt 8", "splashy boot", (map { join(" ", "probe-modules", list_modules::filename2modname($_), grep { $_ } $live->{system}{module_options}{$_}) } @modules), "probe-modules --$media->{storage}", if_($rrpt_dev, "echo *** Waiting for new partitions on device ${rrpt_dev} ***", "sh -c 'while ! ls /sys/block/${rrpt_dev}/${rrpt_dev}* >/dev/null 2>&1; do sleep 3; blockdev --rereadpt /dev/${rrpt_dev} >/dev/null 2>&1; done'"), $debug_shell, if_($pre, deref_array($pre)), "showlabels --removable", MDV::Draklive::StorageFS::get_mount($fs)->($live, $media), (map { $loop_types{$_->{type}}{mount}->($live, $_) } @$mount_first, @$mount_last), ($live->{mount}{overlay} ? $MDV::Draklive::Overlay::overlay{$live->{mount}{overlay}}{mount}->($live) : ()), if_($live->{system}{initrd_pre_pivot}, deref_array($live->{system}{initrd_pre_pivot})), qq(splashy_update "chroot $target"), "echo 0x0100 > /proc/sys/kernel/real-root-dev", "umount /sys", "sh -c 'umount /proc/bus/usb 2>/dev/null'", "umount /proc", "pivot_root $target $target$initrdroot", if_($live->{mount}{root}, "sh -c 'rmdir $initrdroot$live->{prefix}{live}{mnt}$live->{mount}{root}'"), (map { $loop_types{$_->{type}}{pivot_clean}->($live, $_, $initrdroot) } grep { $loop_types{$_->{type}}{pivot_clean} } @{$live->{mount}{dirs} || []}), "sh -c 'cd $initrdroot$live->{prefix}{live}{mnt}; for i in `ls -1`; do [ -d \$i ] || continue; mkdir -p $live->{prefix}{live}{mnt}/\$i; mount -n --move \$i $live->{prefix}{live}{mnt}/\$i; rmdir \$i; done'", "rmdir $initrdroot$live->{prefix}{live}{mnt}", "nash-mount -o mode=0755 -t tmpfs /dev /dev", "sh -c 'rm -rf /dev/loop'", #- not needed after initrd "sh -c 'mv $initrdroot/dev/* /dev/'", if_($live->{system}{initrd_post}, deref_array($live->{system}{initrd_post})), "")); } sub compress_initrd_tree { my ($live, $initrd_tree, $initrd) = @_; my $size = chomp_(run_program::get_stdout("du -ks $initrd_tree | awk '{print \$1}'")); my $inodes = chomp_(run_program::get_stdout("find $initrd_tree | wc -l")) + 100; my $initrd_size = $size + 350 + int($inodes / 10); #- 10 inodes needs 1K $initrd_size += 600; # splashy $initrd =~ s/.gz$//; mkdir_p(dirname($initrd)); run_('dd', 'if=/dev/zero', "of=$initrd", 'bs=1k', "count=$initrd_size"); run_('mke2fs', '-q', '-m', 0, '-F', '-N', $inodes, '-s', 1, $initrd); mkdir_p($live->{mnt}); run_('mount', '-o', 'loop', '-t', 'ext2', $initrd, $live->{mnt}); cp_af(glob("$initrd_tree/*"), $live->{mnt}); rm_rf($live->{mnt} . "/lost+found"); my $left = chomp_(run_program::get_stdout("df -Pk $live->{mnt} | tail -n 1 | awk '{ print \$4 }'")); run_('umount', $live->{mnt}); $left < 200 and die "not enough room to create initrd (only ${left}K left)\n"; run_('gzip', '-f', '-9', $initrd); } sub add_splash { my ($live, $initrd) = @_; if ($live->{system}{vga_mode} && $live->{system}{splash} ne 'no') { require bootloader; my $tmp_initrd = '/tmp/initrd.gz'; cp_f($initrd, $live->get_system_root . $tmp_initrd); { local $::prefix = $live->get_system_root; bootloader::add_boot_splash($tmp_initrd, $live->{system}{vga_mode}); } cp_f($live->get_system_root . $tmp_initrd, $initrd); unlink($live->get_system_root . $tmp_initrd); } } sub create_classical_initrd { my ($live) = @_; my $root = $live->get_system_root; my $kernel = $live->find_kernel; #- FIXME: use bootloader-config and allow it not to require a bootloader? print "using kernel $kernel\n"; my $vmlinuz_long = '/boot/vmlinuz-' . $kernel; my $vmlinuz_short = '/boot/vmlinuz'; symlinkf(basename($vmlinuz_long), $root . $vmlinuz_short); $live->{system}{no_initrd} and return; my $initrd_long = '/boot/initrd-'. $kernel . '.img'; my $initrd_short = '/boot/initrd.img'; run_({}, 'mount', '-t', 'proc', '/proc', $root . '/proc'); run_({}, 'mount', '-t', 'sysfs', '/sys', $root . '/sys'); run_({ root => $root }, 'mkinitrd', '-v', '-f', $initrd_long, $kernel); run_({}, 'umount', $root . '/sys'); run_({}, 'umount', $root . '/proc'); if (-f $root . $initrd_long) { symlinkf(basename($initrd_long), $root . $initrd_short); if ($live->{system}{vga_mode} && $live->{system}{splash} ne 'no') { require bootloader; local $::prefix = $live->get_system_root; bootloader::add_boot_splash($initrd_long, $live->{system}{vga_mode}); } } } 1;