diff options
author | Martin Whitaker <mageia@martin-whitaker.me.uk> | 2017-12-18 10:54:01 +0000 |
---|---|---|
committer | Martin Whitaker <mageia@martin-whitaker.me.uk> | 2017-12-18 10:54:01 +0000 |
commit | 296dc5bb53c430c8842ab7327d8a76b5750c64b4 (patch) | |
tree | 5670e983d5d459466dac2b7a49b7ce14827c7e9d /draklive1 | |
parent | 7e53518a7d8709998482eef1e9da21ee1a1886a9 (diff) | |
download | drakiso-296dc5bb53c430c8842ab7327d8a76b5750c64b4.tar drakiso-296dc5bb53c430c8842ab7327d8a76b5750c64b4.tar.gz drakiso-296dc5bb53c430c8842ab7327d8a76b5750c64b4.tar.bz2 drakiso-296dc5bb53c430c8842ab7327d8a76b5750c64b4.tar.xz drakiso-296dc5bb53c430c8842ab7327d8a76b5750c64b4.zip |
Start creating a new set of tools for generating Mageia ISO images.
The aim is to share as much code as possible between the tool used
to generate the Live ISOs and the tool used to generate the classic
installer ISOs.
This is derived from the user/martinw/use-grub2 branch of draklive.
Diffstat (limited to 'draklive1')
-rwxr-xr-x | draklive1 | 1853 |
1 files changed, 0 insertions, 1853 deletions
diff --git a/draklive1 b/draklive1 deleted file mode 100755 index ad0ce5c..0000000 --- a/draklive1 +++ /dev/null @@ -1,1853 +0,0 @@ -#!/usr/bin/perl - -# draklive $Id: draklive 150793 2007-04-05 12:08:47Z blino $ - -# Copyright (C) 2005 Mandriva -# Olivier Blin <oblin@mandriva.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, 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. - -use lib qw(/usr/lib/libDrakX); -use strict; -use MDK::Common; -use common; -use fs; -use modules; -use run_program; -use Getopt::Long; -use Pod::Usage; -use File::Temp; -use File::Copy qw(mv); -use IO::Handle; #- autoflush -use POSIX; -use MDV::Draklive::Utils; -use MDV::Draklive::Live; -use MDV::Draklive::Progress; -use MDV::Draklive::Loopback; -use MDV::Draklive::Config; -use MDV::Draklive::Storage; - -sub get_syslinux_path { - my ($media, $opts) = @_; - '/' . $media->{storage} . '/syslinux' . ($opts->{boot} && '-boot-' . $opts->{boot}) . '.cfg'; -} - -sub need_media_specific_boot { - my ($live) = @_; - to_bool(list_selected_loopbacks($live)); -} - -sub get_default_append { - my ($live, $opts) = @_; - my $append = $opts->{append} || $live->{system}{append}; - join(' ', - 'root=mgalive:' . $live->{media}->get_media_source_for_nash, - if_($live->{system}{vga_mode} && $append !~ /\bvga=\b/, - 'splash quiet noiswmd audit=0 rd.luks=0 rd.lvm=0 rd.md=0 rd.dm=0', - 'vga=' . $live->{system}{vga_mode}), - if_($append, $append), - ); -} - -sub get_bootloader_timeout { - my ($live) = @_; - defined $live->{media}{bootloader_timeout} ? $live->{media}{bootloader_timeout} : 4; -} - -my @syslinux_boot_files = qw(/vmlinuz /syslinux/bootlogo /help.msg); - -sub build_syslinux_cfg { - my ($live, $media, $opts) = @_; - my $append = get_default_append($live, $opts); - #- syslinux wants files at root (used with vfat fs) - my $to_root = $media->get_boot_setting('fs', $opts) eq 'vfat'; - my $boot = $live->get_media_prefix('boot', $opts->{boot}); - my ($initrd, $kernel, $bootlogo, $help) = map { $to_root ? basename($_) : $_ } - map { $boot . $_ } $media->get_initrd_path, @syslinux_boot_files; - #- the generated syslinux/bootlogo is incomplete, so replace it - my $bootdir = $live->get_builddir . $live->{prefix}{build}{boot}; - my $has_bootlogo = $live->{system}{gfxboot} && -e ($bootdir . '/gfxmenu'); - cp_f($bootdir . '/gfxmenu', $bootdir . '/syslinux/bootlogo') if $has_bootlogo; - my $timeout = get_bootloader_timeout($live) * 10; - my $title = $media->{title} || $live->{media}{title}; - join("\n", - "default $title", - "prompt 1", - "timeout $timeout", - "display $help", - if_($has_bootlogo, "ui gfxboot.c32 $bootlogo"), - (map { - my ($name, $cmdline) = @$_; - $name =~ s/\s/_/g; - if ($name eq "harddisk") { - "label " . $name, - " com32 chain.c32 hd0 exit"; - } else { - "label " . ($name || $title), - " kernel $kernel", - " append initrd=$initrd $append $cmdline"; - } - } group_by2(@{$media->{boot_entries}})), - "", - ); -} - -sub build_grub_cfg { - my ($live, $media, $opts, $device) = @_; - #- FIXME? first partition is hardcoded for loopback (master images) - #- FIXME? use find_partition_index - my ($part_nb) = $device =~ m!/dev/loop! ? 1 : $device =~ /(\d+)$/; - my $part_idx = $part_nb - 1; - my $initrd = $media->get_initrd_path; - build_grub_cfg_raw($live, $media, $initrd, $opts, $part_idx); -} - -sub build_grub_cfg_raw { - my ($live, $media, $initrd, $opts, $part_idx) = @_; - #- FIXME: use the bootloader module from drakx - my $grub_part = "(hd0" . (defined $part_idx ? "," . $part_idx : "") . ")"; - my $boot = $live->get_media_prefix('boot'); #- FIXME handle boot media - #- remove prefix if installing bootloader on separate /boot partition - $media->{partitions}[$part_idx]{mntpoint} eq $boot and $boot = ""; - - my $title = $media->{title} || $live->{media}{title}; - - join("\n", - "timeout " . get_bootloader_timeout($live), - if_($live->{system}{gfxboot}, "gfxmenu $grub_part" . $boot . "/gfxmenu"), - "default 0", - (map { - my ($name, $cmdline) = @$_; - "title " . $title . if_($name, " ($name)"), - "kernel $grub_part" . $boot . "/vmlinuz " . get_default_append($live, $opts) . if_($cmdline, " $cmdline"), - if_($initrd, "initrd " . $boot . $initrd); - } group_by2(@{$media->{boot_entries}})), - ($live->{oem_rescue} && defined $opts->{oem_rescue_idx} ? ( - #- FIXME: factorize with above, build_grub_cfg_entry($media) - "title " . $live->{oem_rescue}{media}{title}, - "kernel (hd0,$opts->{oem_rescue_idx})" . $boot . "/vmlinuz " . $live->{oem_rescue}{append}, - "initrd (hd0,$opts->{oem_rescue_idx})" . $boot . $media->get_initrd_path, - ) : ()), - "", - ); -} - -sub get_langs { - my ($live) = @_; - uniq( - (ref $live->{regions} ? @{$live->{regions}{$live->{settings}{region}}} : ()), - @{$live->{system}{langs_always}} - ); -} - -sub install_system { - my ($live) = @_; - - my $repository = $live->{settings}{repository} . '/' . $live->{settings}{arch}; - - my $drakx_in_chroot = $repository . '/misc/drakx-in-chroot'; - my $remote_repository = $repository =~ m!^(ftp|http)://! && $1; - if ($remote_repository) { - my $local_drakx_in_chroot = $live->get_builddir . $live->{prefix}{build}{scripts} . '/drakx-in-chroot'; - mkdir_p(dirname($local_drakx_in_chroot)); - run_('curl', '--silent', '-o', $local_drakx_in_chroot, $drakx_in_chroot) - or die "unable to get drakx-in-chroot from remote repository\n"; - $drakx_in_chroot = $local_drakx_in_chroot; - } - - local %ENV = ( - %ENV, - (map { "DRAKLIVE_" . uc($_->[0]) => $_->[1] } group_by2(%{$live->{settings}})), - %{$live->{system}{install_env}}, - ); - $ENV{DRAKLIVE_LANGS} = join(':', get_langs($live)); - run_({ targetarch => $live->{settings}{arch} }, - 'perl', $drakx_in_chroot, - $repository, - $live->get_system_root, - if_($live->{system}{auto_install}, '--auto_install', $live->{settings}{config_root} . '/' . $live->{system}{auto_install}), - if_($live->{system}{patch_install}, '--defcfg', $live->{settings}{config_root} . '/' . $live->{system}{patch_install}), - if_($live->{system}{rpmsrate}, '--rpmsrate', $live->{settings}{config_root} . '/' . $live->{system}{rpmsrate}), - ($live->{system}{stage2_updates} ? (map { ('--stage2-update', $live->{settings}{config_root} . '/' . $_->[0], $_->[1]) } @{$live->{system}{stage2_updates}}) : ()), - ) or die "unable to install system chroot\n"; - post_install_system($live); -} - -sub configure_draklive_resize { - my ($live) = @_; - - my $resizable_loopback = find { $_->{min_size} } @{$live->{mount}{dirs} || []}; - if ($resizable_loopback) { - my $media_loopbacks = $live->get_media_prefix('loopbacks'); - my $ext = $loop_types{$resizable_loopback->{type}}{extension}; - output($live->get_system_root . '/etc/sysconfig/draklive-resize', <<EOF); -DRAKLIVE_RESIZE=yes -LOOPBACK=$live->{prefix}{live}{mnt}$live->{prefix}{media}{mnt}${media_loopbacks}$resizable_loopback->{path}$ext -TYPE=$resizable_loopback->{fs} -MIN_SIZE=$resizable_loopback->{min_size} -MOUNT=$live->{prefix}{live}{mnt}$resizable_loopback->{mountpoint}_resized -OLD_MOUNT=$live->{prefix}{live}{mnt}$resizable_loopback->{mountpoint} -UNION=/ -EOF - } -} - -sub copy_files_to { - my ($live, $files, $root) = @_; - foreach (@$files) { - my ($source, $dest, $o_opts) = @$_; - $dest = $root . '/' . $dest; - mkdir_p($dest =~ m|/$| ? $dest : dirname($dest)); - my @sources = MDV::Draklive::Utils::glob__($live->{settings}{config_root} . '/' . $source); - print STDERR "copying @sources to $dest\n"; - cp_af(@sources, $dest); - my $o_perm = $o_opts && $o_opts->{mode}; - chmod $o_perm, $dest if $o_perm; - } -} - -sub join_lists { - my ($separator, $head, @lists) = @_; - @{$head || []}, map { $separator, @$_ } @lists; -} - -sub remove_files_from { - my ($files, $root) = @_; - run_('find', $root, '(', join_lists('-o', map { [ '-name', $_ ] } @$files), ')', '-exec', 'rm', '-r', '{}', ';') - if $files && @$files; -} - -sub clean_system_conf_file { - my ($live, $file) = @_; - substInFile { undef $_ if /^[^#]/ } $live->get_system_root . $file; -} - -sub post_install_system { - my ($live) = @_; - - my $previous_umask = umask; - #- workaround buggy installation of directories that are not owned by any packages - umask 022; - - mount_system_fs($live); - - #- copy resolv.conf for name resolution to work when adding media - cp_f("/etc/resolv.conf", $live->get_system_root . "/etc/"); - - #- remove previous draklive leftovers if needed - run_({ root => $live->get_system_root }, 'urpmi.removemedia', '-a'); - - foreach (@{$live->{system}{additional_media}}) { - run_({ root => $live->get_system_root }, 'urpmi.addmedia', if_($_->{distrib}, '--distrib'), $_->{name}, $_->{path}) - or die "unable to add media from $_->{path}\n"; - @{$_->{packages} || []} or next; - run_({ root => $live->get_system_root, targetarch => $live->{settings}{arch} }, - 'urpmi', '--auto', '--no-verify-rpm', if_(!$_->{distrib}, '--searchmedia', $_->{name}), @{$_->{packages}}) - or die "unable to install packages from $_->{path}\n"; - } - - #- additional rpms may have dependencies in additional media - if (@{$live->{system}{rpms} || []}) { - my $rpm_tmp_dir = '/tmp/draklive_rpms'; - mkdir_p($live->get_system_root . $rpm_tmp_dir); - cp_f((map { $live->{settings}{config_root} . '/' . $_ } @{$live->{system}{rpms}}), $live->get_system_root . $rpm_tmp_dir); - run_({ root => $live->get_system_root, targetarch => $live->{settings}{arch} }, - 'urpmi', '--auto', '--no-verify-rpm', - map { $rpm_tmp_dir . '/' . basename($_) } @{$live->{system}{rpms}}) - or die "unable to install additional system rpms\n"; - rm_rf($live->get_system_root . $rpm_tmp_dir); - } - - #- remove urpmi media added by drakx-in-chroot and additional media, they're unusable - run_({ root => $live->get_system_root }, 'urpmi.removemedia', '-a'); - - my $erase = join(' ', @{$live->{system}{erase_rpms} || []}); - run_({ root => $live->get_system_root, targetarch => $live->{settings}{arch} }, - 'sh', '-c', "rpm -qa $erase | xargs rpm -e ") if $erase; - - run_({ root => $live->get_system_root }, 'systemctl', 'disable', $_ . '.service') foreach @{$live->{system}{disable_services}}; - run_({ root => $live->get_system_root }, 'systemctl', 'disable', $_ . '.timer') foreach @{$live->{system}{disable_timers}}; - - #- make sure harddrake is run: - #- if previous HW config file is empty, we assumes DrakX has just completed the installation - #- (do it in chroot, or else Storable from the build box may write an incompatible config file) - run_({ root => $live->get_system_root }, - 'perl', '-MStorable', '-e', qq(Storable::store({ UNKNOWN => {} }, '/etc/sysconfig/harddrake2/previous_hw'))); - - #- remove some build-machine specific configuration - clean_system_conf_file($live, $_) - foreach qw(/etc/mtab /etc/iftab /etc/shorewall/interfaces /etc/mdadm.conf), - if_(!$live->{system}{skip_fstab}, '/etc/fstab'); - unlink($_) foreach map { glob($live->get_system_root . $_) } @{$live->{system}{remove_files} || []}; - - if ($live->{system}{modules_conf}) { - local $::prefix = $live->get_system_root; - local *modules::write_preload_conf = sub {}; #- FIXME, make this an option - my $modules_conf = modules::any_conf->vnew; - put_in_hash($modules_conf, $live->{system}{modules_conf}); - $modules_conf->write; - } - - my $mount_options = $live->{media}->get_media_setting('mount_options') || "defaults"; - output_with_perm($live->get_system_root . '/etc/fstab', 0644, - $live->{mount}{overlay} - ? "none / $live->{mount}{overlay} $mount_options 0 0\n" - : $live->{media}->get_media_setting('source') . " / " . $live->{media}->get_media_setting('fs') . " $mount_options 1 1\n" - ) unless $live->{system}{skip_fstab}; - - #- interactive mode can lead to race in initscripts - #- (don't use addVarsInSh from MDK::Common, it breaks shell escapes) - substInFile { s/^PROMPT=.*/PROMPT=no/ } $live->get_system_root . '/etc/sysconfig/init'; - - configure_draklive_resize($live); - - if ($live->{system}{preselect_kdm_user}) { - #- preselect specified user in kdm - my $kdm_cfg = $live->get_system_root . '/etc/kde/kdm/kdmrc'; - update_gnomekderc($kdm_cfg, 'X-:0-Greeter' => (PreselectUser => 'Default', DefaultUser => $live->{system}{preselect_kdm_user})) if -f $kdm_cfg; - } - - #- apply patches and install files after the configuration is cleaned - #- to allow special configuration files (especially modprobe.preload) - foreach (@{$live->{system}{patches}}) { - my $patch = $live->{settings}{config_root} . '/' . $_; - my @args = ('-p0', '-d', $live->get_system_root, '-i', $patch); - run_program::run('patch', '>', '/dev/null', '--dry-run', '-f', '-R', @args) || run_('patch', @args) - or die "unable to apply patch " . $patch . "\n"; - } - - copy_files_to($live, $live->{system}{files}, $live->get_system_root); - my @no_install_files = map { $_->[1] } grep { $_->[2] && $_->[2]{no_install} } @{$live->{system}{files}}; - output_p($live->get_system_root . '/etc/draklive-install.d/remove.d/draklive', map { "$_\n" } @no_install_files); - - eval { rm_rf($live->get_builddir . $live->{prefix}{build}{files}) }; - mkdir_p($live->get_builddir . $live->{prefix}{build}{files}); - if ($live->{media}{files}) { - copy_files_to($live, $live->{media}{files}, $live->get_builddir . $live->{prefix}{build}{files}); - } - remove_files_from($live->{media}{remove_files}, $live->get_builddir . $live->{prefix}{build}{files}); - - run_({ targetarch => $live->{settings}{arch} }, - "chroot", $live->get_system_root, "bash", "-c", $live->{system}{postInstall}) if $live->{system}{postInstall}; - - clean_system_conf_file($live, "/etc/resolv.conf"); - write_dist_lists($live); - - umount_system_fs($live); - - umask $previous_umask; -} - -sub write_dist_lists { - my ($live) = @_; - - my $lists_dir = $live->get_builddir . $live->{prefix}{build}{dist}; - mkdir_p($lists_dir); - - run_("chroot " . $live->get_system_root . " rpm -qa | sort > " . - $lists_dir . '/' . $live->get_name . '.lst'); - - run_("chroot " . $live->get_system_root . " rpm -qa --qf '%{name}\n' | sort > " . - $lists_dir . '/' . $live->get_name . '.lst.names'); - - run_("chroot " . $live->get_system_root . - qq( sh -c "rpm -qa --qf '[%{NAME} %{FILESIZES} %{FILESTATES}\n]' | awk '{if(\\\$3==0) {s[\\\$1]+=\\\$2}} END{for (p in s){print s[p],p}}' | sort -n" > ) . - $lists_dir . '/' . $live->get_name . '.lst.full'); - - run_("chroot " . $live->get_system_root . - qq( sh -c "urpmi_rpm-find-leaves | xargs rpm -q --qf '[%{NAME} %{FILESIZES} %{FILESTATES}\n]' | awk '{if(\\\$3==0) {s[\\\$1]+=\\\$2}} END{for (p in s){print s[p],p}}' | sort -n" > ) . - $lists_dir . '/' . $live->get_name . '.lst.leaves'); - - require lang; - my @live_langs = get_langs($live); - my @langs = grep { member($_, @live_langs) || member(lang::locale_to_main_locale($_), @live_langs) } lang::list_langs(); - my $langs_file = $lists_dir . '/' . $live->get_name . '.langs'; - output_p($langs_file, map { lang::l2name($_) . " (" . $_ . ")\n" } sort(@langs)); -} - -sub mount_system_fs { - my ($live) = @_; - run_('mount', '-t', 'devtmpfs', '/dev', $live->get_system_root . '/dev'); - run_('mount', '-t', 'proc', '/proc', $live->get_system_root . '/proc'); - run_('mount', '-t', 'sysfs', '/sys', $live->get_system_root . '/sys'); - run_('mount', '-t', 'debugfs', '/sys/kernel/debug/usb', $live->get_system_root . '/sys/kernel/debug/usb'); -} - -sub umount_system_fs { - my ($live) = @_; - eval { fs::mount::umount($live->get_system_root . $_) } foreach qw(/dev /proc /run /sys/kernel/debug/usb /sys); -} - -sub umount_external_fs { - my ($live) = @_; - eval { fs::mount::umount($live->get_system_root . $_) } foreach map { "/mnt/$_" } all($live->get_system_root . "/mnt"); -} - -sub expand_file_list { - my ($live, @files) = @_; - map { - $_->{path} ? - $_->{path} : - chomp_(cat_(glob(($_->{rooted} && $live->get_system_root) . $_->{source}))); - } @files; -} - -#- hardlink recursively file list to a directory -sub hardlink_filtered { - my ($src, $dest, $files) = @_; - mkdir_p($dest); - my $pwd = $ENV{PWD}; - chdir($src); - my $list_file = tmpnam(); - output_p($list_file, map { "$_\n" } grep { -e $src . $_ } @$files); - #- cpio -pldm won't copy recursively, use rsync -r instead - system('rsync', '-ar', '--files-from=' . $list_file, '--link-dest=' . $src, $src, $dest); - unlink $list_file; - chdir($pwd); -} - -sub list_loopback_modules { - my ($live) = @_; - map { - my $l = $_; - map { - my $list = $_; - my $name = basename($list); - $name =~ s/\.[^.]+$//; - { type => $l->{type}, name => $name, files => [ expand_file_list($live, { source => $list }) ] }; - } glob(($_->{rooted} && $live->get_system_root) . $_->{source}); - } @{$live->{loopbacks}{modules}}; -} - -sub create_loopback_files { - my ($live) = @_; - # umount filesystem in the live before creating the loopback - umount_external_fs($live); - umount_system_fs($live); - - my @excluded_files = expand_file_list($live, @{$live->{loopbacks}{exclude}{files} || []}); - my @modules_files = expand_file_list($live, @{$live->{loopbacks}{modules} || []}); - - foreach (grep { exists $loop_types{$_->{type}}{build} } @{$live->{mount}{dirs} || []}) { - local $_->{exclude} = [ @excluded_files, @modules_files ]; - $loop_types{$_->{type}}{build}->($live, $_); - } - - foreach my $module (list_loopback_modules($live)) { - my $copy_tree = $live->get_system_root . "/tmp/draklive/loop/$module->{name}"; - eval { rm_rf($copy_tree) }; - hardlink_filtered($live->get_system_root, $copy_tree, $module->{files}); - my $loop = $loop_types{$module->{type}}; - $loop->{build}->($live, { path => "$live->{prefix}{build}{modules}/$module->{name}", root => $copy_tree, exclude => \@excluded_files }); - eval { rm_rf($copy_tree) }; - } - - if (@excluded_files) { - my $excluded_tree = $live->get_system_root . "/tmp/draklive/excluded/all"; - eval { rm_rf($excluded_tree) }; - hardlink_filtered($live->get_system_root, $excluded_tree, \@excluded_files); - - foreach my $module (list_loopback_modules($live)) { - my $copy_tree = $live->get_system_root . "/tmp/draklive/excluded/$module->{name}"; - eval { rm_rf($copy_tree) }; - hardlink_filtered($excluded_tree, $copy_tree, $module->{files}); - my $loop = $loop_types{$module->{type}}; - $loop->{build}->($live, { path => "$live->{prefix}{build}{modules}/excluded-$module->{name}", root => $copy_tree }); - eval { rm_rf($copy_tree) }; - } - - my $loop = $loop_types{$live->{loopbacks}{exclude}{type}}; - $loop->{build}->($live, { path => "/excluded", root => $excluded_tree, exclude => \@modules_files }); - - eval { rm_rf($excluded_tree) }; - } -} - -sub list_selected_loopbacks { - my ($live) = @_; - my @pack = $live->{settings}{pack} ? @{$live->{packs}{$live->{settings}{pack}} || []} : (); - my @pack_modules = grep { member($_->{name}, @pack) } list_loopback_modules($live); - (map { $loop_types{$_->{type}}{is_loopback} && $_->{path} ? $_->{path} . $loop_types{$_->{type}}{extension} : () } @{$live->{mount}{dirs} || []}), - (map { $live->{prefix}{build}{modules} . '/' . $_->{name} . $loop_types{$_->{type}}{extension} } @pack_modules); -} - -sub get_media_device { - my ($live, $opts) = @_; - return $opts->{device} if $opts->{device}; - my $label = $live->{media}->get_media_label - or die "no device and no label"; - my $device = chomp_(`readlink -f /dev/disk/by-label/$label`) - or die "unable to find device for /dev/disk/by-label/$label\n"; - $device; -} - -sub prepare_bootloader { - my ($live) = @_; - create_initrd($live); - create_bootloader($live) if !($live->{system}{skip_bootloader_config} || $live->{system}{skip_bootloader_install}); -} - -sub create_initrd { - my ($live) = @_; - my $root = $live->get_system_root; - - mount_system_fs($live); - - my $kernel = $live->find_kernel; - print "using kernel $kernel->{version}\n"; - - my $initrd_file = $root . '/boot/' . $live->get_initrd_name; - unlink($initrd_file); - - { - my $bootloader = {}; - local $::prefix = $root; - bootloader::add_kernel($bootloader, $kernel, { label => 'linux', vga => $live->{system}{vga_mode} }, '', $live->{system}{no_initrd}); - } - - umount_system_fs($live); - - if (need_media_specific_boot($live)) { - my $rootfs_initrd = $root . '/boot/' . $live->get_initrd_name; - my $media_initrd = $live->get_builddir . $live->{prefix}{build}{boot} . $live->{media}->get_initrd_path; - mkdir_p(dirname($media_initrd)); - mv($rootfs_initrd, $media_initrd) or die "can not move initrd: $!"; - } -} - -sub create_bootloader { - my ($live) = @_; - - my $root = $live->get_system_root; - my $kernel = $live->find_kernel->{version}; - my $vmlinuz_long = '/boot/vmlinuz-' . $kernel; - -e $root . $vmlinuz_long or die "can not find kernel $kernel\n"; - - if ($live->{system}{gfxboot}) { - mount_system_fs($live); - - #- would be run by bootloader::add_boot_splash and make-boot-splash, but not called when we don't generate an initrd - run_({ root => $root }, '/usr/share/bootsplash/scripts/switch-themes', '-u'); - #- grub-gfxmenu would be run by bootloader::write_grub from DrakX - run_({ root => $root }, '/usr/sbin/grub-gfxmenu', '--update-gfxmenu'); - my $gfxmenu = $root . '/boot/gfxmenu'; - if (-e $gfxmenu) { - my $boot_dir = $live->get_builddir . $live->{prefix}{build}{boot}; - mkdir_p($boot_dir); - cp_f($gfxmenu, $boot_dir); - } else { - warn "no gfxmenu file\n"; - } - - umount_system_fs($live); - } - - # this will copy (among other things) the gfxboot theme to the media - # so this must be done before the creating the bootloader since the code - # in there may check if a bootlogo is present or not - create_syslinux_msg_files($live); - - if (need_media_specific_boot($live)) { - create_media_bootloader($live); - } else { - create_classical_bootloader($live); - } - - create_efi_boot_image($live) if $live->{settings}{arch} eq 'x86_64'; -} - -sub remove_unneeded_bootlogo_locales { - use File::Temp; - use Cwd; - - my ($bootlogo, @locales) = @_; - - $bootlogo = Cwd::realpath($bootlogo); - -f $bootlogo or return; - - my $cwd = Cwd::getcwd(); - my $tempdir = File::Temp::tempdir("mgagfxbootXXXX", CLEANUP => 1); - chdir $tempdir; - !system("cpio -id < $bootlogo") or return; - - # Make sure we include the en locale - push @locales, 'en'; - my @kept_locales; - foreach my $file (glob "*.tr") { - if (!any { $file =~ /^$_\.tr$/ } @locales) { - unlink $file; - } else { - my ($locale_name) = $file =~ /(.*)\.tr$/; - push @kept_locales, $locale_name; - } - } - system(qq(echo init 16x16.fnt *.tr |sed "s/ /\\n/g" |cpio -o >$bootlogo)); - chdir $cwd; - - print "gfxboot locales: " . join(" ", @kept_locales) . "\n"; - return @kept_locales; -} - -#- forked from bootsplash::themes_read_sysconfig -sub get_bootsplash_theme() { - my $sysconfig_file = "/etc/sysconfig/bootsplash"; - local $_; - my %theme; - foreach (cat_($::prefix . $sysconfig_file)) { - /^THEME=(.*)/ and $theme{name} = $1; - } - \%theme; -} - -sub create_syslinux_msg_files { - my ($live) = @_; - my $syslinux_dir = $live->get_builddir . $live->{prefix}{build}{boot} . '/syslinux'; - mkdir_p($syslinux_dir); - - if ($live->{system}{gfxboot}) { - my $product = common::parse_LDAP_namespace_structure(cat_($live->get_system_root . '/etc/product.id')); - my $default_gfxboot_theme = $product->{distribution}; - my $theme = do { - local $::prefix = $live->get_system_root; - get_bootsplash_theme(); - }; - print "copying $default_gfxboot_theme gfxboot theme\n"; - cp_f(glob_($live->get_system_root . "/usr/share/gfxboot/themes/$default_gfxboot_theme/install/*"), $syslinux_dir); - if ($theme->{name} ne $default_gfxboot_theme) { - print "copying $theme->{name} gfxboot theme\n"; - cp_f(glob_($live->get_system_root . "/usr/share/gfxboot/themes/$theme->{name}/*"), $syslinux_dir); - } - my $bootlogo = $syslinux_dir . '/bootlogo'; - warn "unable to find gfxboot splash ($bootlogo)\n" if ! -f $bootlogo; - my @locales = remove_unneeded_bootlogo_locales($bootlogo, get_langs($live)); - output_p($syslinux_dir . '/langs', join("\n", @locales) . "\n"); - output_p($syslinux_dir . '/gfxboot.cfg', join("\n", - "livecd=1", - "mainmenu.pos=210,235", - "mainmenu.bar.minwidth=400", - "panel.f-key.fg=0x33358c", - "", - )); - } - - output($live->get_builddir . $live->{prefix}{build}{boot} . '/help.msg', - qq( -Welcome to $live->{media}{title}! - -The command line can be used to specify kernel options. - -$live->{media}{title} <kernel options> - -)); -} - -sub create_efi_boot_image { - my ($live) = @_; - my $src_dir = $live->get_builddir . $live->{prefix}{build}{files} . '/EFI'; - my $dst_dir = $live->get_builddir . $live->{prefix}{build}{files} . '/boot'; - my $label = $live->{media}->get_media_label; - run_("sed", "-i", "s/VOLUME_LABEL/$label/g", $src_dir . '/BOOT/grub.cfg'); - mkdir_p($dst_dir); - my $boot_image = $dst_dir . '/efiboot.img'; - eval { rm_rf($boot_image) }; - run_("/sbin/mkdosfs", "-F12", "-C", $boot_image, "4096"); - run_("mcopy", "-s", "-i", $boot_image, $src_dir, "::"); -} - -sub create_media_bootloader { - my ($live) = @_; - cp_f($live->get_system_root . '/boot/vmlinuz-' . $live->find_kernel->{version}, $live->get_builddir . $live->{prefix}{build}{boot} . '/vmlinuz'); - foreach my $boot ('', @{$live->{media}{extra_boot}}) { - my $opts = { boot => $boot }; - output($live->get_builddir . $live->{prefix}{build}{boot} . get_syslinux_path($live->{media}, $opts), - build_syslinux_cfg($live, $live->{media}, $opts)); - } -} - -sub create_classical_bootloader { - my ($live) = @_; - my $initrd_prefix = "/initrd.img"; - my $initrd = $live->get_system_root . $live->get_media_prefix('boot') . $initrd_prefix; - my $part_idx = $live->{media}->find_boot_partition_index; - my $oem_rescue_idx = $live->{media}->find_partition_index('OEM_RESCUE'); - output_p($live->get_system_root . '/boot/grub/menu.lst', build_grub_cfg_raw($live, $live->{media}, -e $initrd && $initrd_prefix, { oem_rescue_idx => $oem_rescue_idx }, $part_idx)); -} - -sub create_tarball { - my ($live) = @_; - run_("tar", "cjf", get_disk_master_prefix($live) . ".tar.bz2", $live->get_system_root); -} - -sub set_device_label { - my ($device, $type, $label) = @_; - if ($type eq 'vfat') { - MDV::Draklive::Utils::mtools_run_('mlabel', '-i', $device, '::' . $label); - } elsif (member($type, 'ext2', 'ext3')) { - run_('e2label', $device, $label); - } else { - die "unable to set label for unsupported media type $type\n"; - } -} - -sub get_cdrom_master_path { - my ($live, $opts) = @_; - $live->get_builddir . $live->{prefix}{build}{dist} . '/' . $live->get_name . ($opts->{boot} && "-boot-$opts->{boot}") . '.iso'; -} - -sub get_cdrom_replicator_path { - my ($live) = @_; - get_disk_replicator_prefix($live) . ".iso"; -} - -sub create_cdrom_master { - my ($live, $opts) = @_; - my $label = $live->{media}->get_media_label or die "the source device must be described by a label\n"; - my $dest; - unless ($opts->{onthefly}) { - $dest = get_cdrom_master_path($live, $opts); - mkdir_p(dirname($dest)); - } - build_iso_image( - $live, - $dest, - $live->get_builddir . $live->{prefix}{build}{boot} . get_syslinux_path($live->{media}, $opts), - $label, - $live->get_media_prefix('boot', $opts->{boot}) . '=' . $live->get_builddir . $live->{prefix}{build}{boot}, - if_(!$opts->{boot_only}, - (map { - $live->get_media_prefix('loopbacks', $opts->{boot}) . $_ . - '=' . - $live->get_builddir . $live->{prefix}{build}{loopbacks} . $_; - } list_selected_loopbacks($live)), - if_($live->{media}{files}, - map { - $_ . '=' . $live->get_builddir . $live->{prefix}{build}{files} . '/' . $_; - } all($live->get_builddir . $live->{prefix}{build}{files}) - ), - ), - ); -} - -sub build_iso_image { - my ($live, $dest, $isolinux_cfg, $label, @opts) = @_; - - my $progress = MDV::Draklive::Progress->new(100, time()); - my $in_progress; - autoflush STDOUT 1; - local $_; - run_foreach(sub { - if (/^\s*([0-9.]+)%\s*done,/) { - $progress->{current} = int($1); - $progress->show(time()); - $in_progress = 1; - } else { - print "\n" if $in_progress; - print $_; - $in_progress = 0; - } - }, - 'xorriso', '-as', 'mkisofs', '-pad', '-l', '-R', '-J', - '-V', $label, #'-A', $application, '-p', $preparer, '-P', $publisher, - '-b', 'isolinux/isolinux.bin', - '-c', 'isolinux/boot.cat', - '-hide-rr-moved', '-no-emul-boot', - '-boot-load-size', 4, '-boot-info-table', - '-graft-points', - if_($live->{settings}{arch} =~ /x86_64/, '-eltorito-alt-boot', '-e', 'boot/efiboot.img', '-no-emul-boot'), - if_($dest, '-o', $dest), - 'isolinux=' . $live->get_builddir . $live->{prefix}{build}{boot} . '/syslinux', - 'isolinux/isolinux.cfg=' . $isolinux_cfg, - 'isolinux/isolinux.bin=/usr/lib/syslinux/isolinux.bin', - 'isolinux/chain.c32=/usr/lib/syslinux/chain.c32', - 'isolinux/gfxboot.c32=/usr/lib/syslinux/gfxboot.c32', - 'isolinux/ldlinux.c32=/usr/lib/syslinux/ldlinux.c32', - 'isolinux/libcom32.c32=/usr/lib/syslinux/libcom32.c32', - 'isolinux/libgpl.c32=/usr/lib/syslinux/libgpl.c32', - 'isolinux/libmenu.c32=/usr/lib/syslinux/libmenu.c32', - 'isolinux/libutil.c32=/usr/lib/syslinux/libutil.c32', - @opts, - ) or die "unable to run xorriso\n"; - autoflush STDOUT 0; - $progress->end; - if ($dest) { - my $dir = dirname($dest); - my $filename = basename($dest); - run_('isohybrid', ($live->{settings}{arch} =~ /x86_64/ ? '-u' : '-o 1'), $dest); - run_('mgaiso-addmd5', '>', '/dev/null', '2>', '/dev/null', $dest); - run_({ chdir => $dir }, 'md5sum', '>', $dest . '.md5', $filename); - run_({ chdir => $dir }, 'sha1sum', '>', $dest . '.sha1', $filename); - run_({ chdir => $dir }, 'sha512sum', '>', $dest . '.sha512', $filename); - run_({ chdir => $dir }, 'date', '>', $dir . '/DATE.txt'); - if (my $suffix = $live->get_set_suffix) { - if (my ($prefix, $ext) = $dest =~ /(.*)(\.[^.]+)$/) { - my $link = $prefix . $suffix . $ext; - linkf($dest, $link); - print "linked as $link\n"; - } - } - } -} - -sub get_disk_master_prefix { - my ($live) = @_; - $live->get_builddir . $live->{prefix}{build}{dist} . '/' . $live->get_name; -} - -sub get_disk_master_path { - my ($live) = @_; - get_disk_master_prefix($live) . '.img'; -} - -sub get_partition_loop { - my ($hd, $part) = @_; - require devices; - my $loop = devices::find_free_loop(); - run_('losetup', '-o', $part->{start} * $common::SECTORSIZE, '-s', $part->{size} * $common::SECTORSIZE, $loop, $hd->{file}) - or die "unable to setup loop device"; - return $loop; -} - -sub get_harddisk_geometry { - my ($media) = @_; - $media->{geom} || { - heads => 16, - sectors => 63, # sectors per track - }; -} - -sub get_hd_from_layout { - my ($media, $dest) = @_; - my $geom = get_harddisk_geometry($media); - my $required_sectors = fold_left { $::a + $::b } map { $_->{size} } @{$media->{partitions}}; - $required_sectors += $geom->{sectors}; # keep one more track - $geom->{cylinders} = POSIX::ceil($required_sectors / ($geom->{sectors} * $geom->{heads})); - my $total_sectors = $geom->{cylinders} * $geom->{heads} * $geom->{sectors}; - bless { - totalsectors => $total_sectors, - geom => $geom, - file => $dest, - }, 'partition_table::dos'; -} - -sub get_hd_from_file { - my ($media, $file) = @_; - my $hd = bless { - geom => get_harddisk_geometry($media), - file => $file, - }, 'partition_table::dos'; - partition_table::read($hd); - return $hd; -} - -sub supplement_media_partitions { - my ($media, $hd) = @_; - #- try to find additional partition details (start, device) - #- by matching actual partition table and partitions list - my @all_parts = partition_table::get_normal_parts($hd); - foreach my $idx (0 .. $#all_parts) { - $media->{partitions}[$idx]{$_} = $all_parts[$idx]{$_} foreach qw(start device); - } -} - -sub set_part_real_device { - my ($hd, $part) = @_; - #- FIXME: find a better way to compute mmcblk device path - my $ext = $hd->{file} =~ m!^/dev/mmcblk! ? 'p' : ''; - $part->{real_device} = -f $hd->{file} ? get_partition_loop($hd, $part) : ($hd->{file} . $ext . $part->{device}); -} - -sub allocate_master { - my ($media, $opts) = @_; - - $media->supplement_slash_size($opts->{slash_size}) if $opts->{slash_size}; - my $hd = get_hd_from_layout($media, $opts->{device}); - - mkdir_p(dirname($opts->{device})); - MDV::Draklive::Utils::device_allocate_file($opts->{device}, $hd->{totalsectors} * $common::SECTORSIZE); -} - -sub format_master { - my ($media, $opts) = @_; - - $media->supplement_slash_size($opts->{slash_size}) if $opts->{slash_size}; - - my $hd = get_hd_from_layout($media, $opts->{device}); - partition_table::raw::zero_MBR($hd); - - #- FIXME: maybe use fsedit::allocatePartitions to factorize even more? - foreach my $part (@{$media->{partitions}}) { - my $hole = find { fs::type::isEmpty($_) && $_->{size} >= $part->{size} } partition_table::get_normal_parts_and_holes($hd) - or die "not enough room for $part->{mntpoint}"; - $part->{start} = $hole->{start}; - fs::type::set_fs_type($part, $part->{fs_type}); - partition_table::add($hd, $part, 'Primary'); - } - - print "writing partition table\n"; - partition_table::write($hd); - #- FIXME: move out from diskdrake::interactive::write_partitions to partition_table::write ? - run_program::run('udevadm', 'settle'); - - my $inode_size = $media->get_media_setting('inode_size'); - foreach my $part (@{$media->{partitions}}) { - set_part_real_device($hd, $part); - MDV::Draklive::Utils::device_mkfs($part->{real_device}, $part->{fs_type}, $part->{device_LABEL}, $inode_size) - or die "unable to format $part->{real_device} in $hd->{file}\n"; - devices::del_loop($part->{real_device}) if -f $hd->{file}; - } -} - -sub format_disk { - my ($live, $opts) = @_; - local $opts->{slash_size} = guess_disk_master_size($live); - format_master($live->{media}, $opts); -} - -sub guess_disk_master_size { - my ($live) = @_; - - # slash_size: - @{$live->{mount}{dirs} || []} ? - (directory_usage($live->get_builddir . $live->{prefix}{build}{loopbacks}, 'apparent') + - directory_usage($live->get_builddir . $live->{prefix}{build}{boot}) + - directory_usage($live->get_builddir . $live->{prefix}{build}{files}) - ) : - directory_usage($live->get_system_root); -} - -sub create_disk_master { - my ($live, $opts) = @_; - local $opts->{slash_size} = guess_disk_master_size($live); - local $opts->{device} = get_disk_master_path($live); - allocate_master($live->{media}, $opts); - format_master($live->{media}, $opts); - record_master($live, $opts); -} - -#- $opts: -#- media: alternate media -#- onthefly : if true, the create function must output to stdout -sub create_master { - my ($live, $opts) = @_; - - if (my $create = $live->{media}->get_boot_setting('create', $opts)) { - $create->($live, $opts); - } else { - warn "not implemented yet\n"; - } -} - -sub maybe_umount_device { - my ($device) = @_; - run_('umount', $device) if cat_('/proc/mounts') =~ m!^$device\s+!m; -} - -sub format_cdrom_device { - my ($live, $opts) = @_; - run_('wodim', '-v', 'dev=' . get_media_device($live, $opts), "blank=fast"); -} - -#- $opts: -#- media: alternate media -sub format_device { - my ($live, $opts) = @_; - - get_media_device($live, $opts) or die "no device defined in media configuration\n"; - if (my $format = $live->{media}->get_boot_setting('format', $opts)) { - $format->($live, $opts); - } else { - warn "not implemented yet\n"; - } -} - -sub record_cdrom_path { - my ($live, $path, $opts) = @_; - my $device = get_media_device($live, $opts) - or die "no device defined in media configuration\n"; - - #- CD-Rom images can be hybrid, thus handle recording on both CD-Rom and disks - $device = basename(expand_symlinks($device)); - my $sysfs_device = "/sys/block/$device/capability"; - #- GENHD_FL_CD is 8 (include/linux/genhd.h) - my $is_cdrom = !-e $sysfs_device || hex(cat_($sysfs_device)) & 8; - - if ($is_cdrom) { - my $src = $opts->{onthefly} ? '-' : $path; - run_('wodim', '-v', 'dev=' . $device, $src); - } else { - run_('dd', if_(!$opts->{onthefly}, "if=$path"), "of=$device", "bs=2M"); - } -} - -sub record_cdrom_master { - my ($live, $opts) = @_; - record_cdrom_path($live, get_cdrom_master_path($live, $opts), $opts); -} - -sub record_cdrom_replicator { - my ($live, $opts) = @_; - record_cdrom_path($live, get_cdrom_replicator_path($live), $opts); -} - -sub rsync_delete_options { - my ($opts) = @_; - $opts->{keep_files} ? () : '--delete'; -} - -sub install_grub_to_image { - my ($live, $media, $img, $opts) = @_; - my $media_boot = $live->get_media_prefix('boot', $opts->{boot}); - my $grub_dir = "$media_boot/grub"; - my $grub_script = $grub_dir . "/install.sh"; - my $grub_src = first(glob_($live->get_system_root . "/lib/grub/*-mageia")); - mkdir_p($live->{mnt} . $grub_dir); - cp_af(glob_("$grub_src/*"), $live->{mnt} . $grub_dir); - - my $part_idx = $media->find_boot_partition_index; - my $grub_prefix = $media->find_partition_index('/boot') ? "/grub" : $grub_dir; - - open(my $grub, "| /sbin/grub --batch --no-floppy"); - # using disk loopback fails, have to use image path - print $grub <<EOF; -device (hd0) $img -root (hd0,$part_idx) -setup --prefix=$grub_prefix (hd0) -quit -EOF - close($grub) or die "unable to run grub\n"; - - output($live->{mnt} . $grub_script, <<EOG); -grub --device-map=$media_boot/grub/device.map --batch <<EOF -root (hd0,$part_idx) -setup --stage2=$media_boot/grub/stage2 (hd0) -quit -EOF -EOG - - chmod 0755, $live->{mnt} . $grub_script; -} - -sub install_disk_bootloader { - my ($live, $media, $boot_device, $opts) = @_; - - return if $live->{system}{skip_bootloader_install}; - - my $media_boot = $live->get_media_prefix('boot', $opts->{boot}); - my $device = get_media_device($live, $opts); - my $bootloader = $media->get_boot_setting('bootloader', $opts); - - member($bootloader, 'grub', 'syslinux') or die "no bootloader defined in media configuration\n"; - if ($bootloader eq 'syslinux') { - cp_f($live->get_builddir . $_, $live->{mnt}) foreach map { - $live->{prefix}{boot} . $_; - } get_syslinux_path($media, $opts), $media->get_initrd_path, @syslinux_boot_files; - } elsif ($bootloader eq 'grub') { - if (need_media_specific_boot($live) || $opts->{force_bootloader_config}) { - #- FIXME: add get_grub_path (when building boot configuration files) - # and get_bootloader_path (when copying) - mkdir_p($live->{mnt} . $media_boot . '/grub'); - cp_f($live->get_builddir . $live->{prefix}{build}{boot} . '/gfxmenu', $live->{mnt} . $media_boot) if $live->{system}{gfxboot}; - output_p($live->{mnt} . $media_boot . '/grub/menu.lst', build_grub_cfg($live, $media, $opts, $boot_device)); - } - } - - if (-b $boot_device) { - if ($bootloader eq 'syslinux') { - #- use syslinux -s, "safe, slow and stupid" version of SYSLINUX, unless specified otherwise - run_('syslinux', if_(!$media->{fast_syslinux}, '-s'), $boot_device) - or die "unable to run syslinux on $device\n"; - } elsif ($bootloader eq 'grub') { - install_grub_to_image($live, $media, $device, $opts); - } - } else { - warn "not running $bootloader on non block device $device\n"; - } -} - -sub record_usb_master { - my ($live, $opts) = @_; - my $media = $live->{media}; - my $media_boot = $live->get_media_prefix('boot', $opts->{boot}); - my $media_loopbacks = $live->get_media_prefix('loopbacks', $opts->{boot}); - - my $main_device = get_media_device($live, $opts) - or die "unable to find recording device (missing label? try with --device <device>)\n"; - - my $hd = get_hd_from_file($media, $main_device); - supplement_media_partitions($media, $hd); - - my $slash_idx = $media->find_partition_index('/'); - my $slash = $media->{partitions}[$slash_idx]; - set_part_real_device($hd, $slash); - - if (my $label = !$opts->{boot_only} && $media->get_media_label) { - set_device_label($slash->{real_device}, $media->get_media_setting('fs'), $label); - } - - mkdir_p($live->{mnt}); - run_('mount', $slash->{real_device}, $live->{mnt}) - or die "unable to mount $slash->{real_device}\n"; - - rm_rf($live->{mnt} . $media_boot) if -e $live->{mnt} . $media_boot; - cp_af($live->get_builddir . $live->{prefix}{build}{boot}, $live->{mnt} . $media_boot); - - install_disk_bootloader($live, $media, $slash->{real_device}, $opts); - - do { - my $loopbacks_source = $live->get_builddir . $live->{prefix}{build}{loopbacks} . '/'; - my $total = directory_usage($loopbacks_source); - my $list_file = tmpnam(); - output_p($list_file, map { ".$_\n" } list_selected_loopbacks($live)); - local $/ = "\r"; - my $r = run_foreach(update_progress_rsync($live, $total), - 'rsync', '-vdP', '--inplace', '--files-from=' . $list_file, rsync_delete_options($opts), - $loopbacks_source, $live->{mnt} . $media_loopbacks, - ); - unlink $list_file; - if (!$r) { - run_('umount', $slash->{real_device}); - maybe_umount_device($slash->{real_device}); - devices::del_loop($slash->{real_device}) if -f $hd->{file}; - die "unable to copy loopback files\n"; - } - - cp_af(glob_($live->get_builddir . $live->{prefix}{build}{files} . '/*'), $live->{mnt}); - } unless $opts->{boot_only}; - - my @hidden_files = map { basename($_) } glob_($live->{mnt} . "/.*"), glob_($live->{mnt} . "/autorun.*"); - - run_('umount', $slash->{real_device}); - maybe_umount_device($slash->{real_device}); - - if ($media->get_media_setting('fs') eq 'vfat') { - MDV::Draklive::Utils::mtools_run_('mattrib', '+h', '-i', $slash->{real_device}, '::' . $_) foreach @hidden_files; - MDV::Draklive::Utils::mtools_run_('mattrib', '+r', '+s', '-/', '-i', $slash->{real_device}, '::' . $_) - foreach $media_boot, $media_loopbacks; - } - - devices::del_loop($slash->{real_device}) if -f $hd->{file}; -} - -sub record_harddisk_master { - my ($live, $opts) = @_; - - my $media = $live->{media}; - - my $main_device = get_media_device($live, $opts) - or die "unable to find recording device (missing label? try with --device <device>)\n"; - - my $hd = get_hd_from_file($media, $main_device); - supplement_media_partitions($media, $hd); - my @partitions = grep { $_->{mntpoint} =~ m!^/! } @{$media->{partitions}}; - - mkdir_p($live->{mnt}); - foreach my $part (sort { $a->{mntpoint} cmp $b->{mntpoint} } @partitions) { - set_part_real_device($hd, $part); - my $mnt = $live->{mnt} . $part->{mntpoint}; - mkdir_p($mnt); - run_('mount', $part->{real_device}, $mnt) - or die "unable to mount $part->{real_device}\n"; - } - - my $r = 1; - do { - my $source = $live->get_system_root; - my $total = directory_usage($source); - local $/ = "\r"; - $r = run_foreach(update_progress_rsync($live, $total), 'rsync', rsync_delete_options($opts), '-a', $source . '/', $live->{mnt}) - or last; - } unless $opts->{boot_only}; - - my $boot_idx = $media->find_boot_partition_index; - my $boot_part = $media->{partitions}[$boot_idx]; - install_disk_bootloader($live, $media, $boot_part->{real_device}, $opts); - - foreach my $part (sort { $b->{mntpoint} cmp $a->{mntpoint} } @partitions) { - run_('umount', $part->{real_device}); - maybe_umount_device($part->{real_device}); - devices::del_loop($part->{real_device}) if -f $hd->{file}; - } - - $r or die "unable to copy system files\n"; - - record_oem_rescue($live, $opts) if $live->{oem_rescue}; -} - -#- $opts: -#- onthefly : if true, the record function must read from stdin -sub record_master { - my ($live, $opts) = @_; - - if (my $record = $live->{media}->get_boot_setting('record', $opts)) { - $record->($live, $opts); - } else { - warn "not implemented yet\n"; - } -} - -#- $opts: -#- onthefly : if true, the record function must read from stdin -sub record_replicator { - my ($live, $opts) = @_; - - my $replicator_media = $live->{replicator}{media} or die "no replicator media"; - if (my $record_replicator = $replicator_media->get_boot_setting('record_replicator', $opts)) { - $record_replicator->($live, $opts); - } else { - warn "not implemented yet\n"; - } -} - -sub pipe_subs { - my ($writer, $reader) = @_; - my ($r, $w) = POSIX::pipe; - if (my $pid = fork()) { - POSIX::close($w) or die "couldn't close: $!\n"; - my $stdin = POSIX::dup(0) or die "couldn't dup: $!\n"; - POSIX::dup2($r, 0) or die "couldn't dup2: $!\n"; - POSIX::close($r); - $reader->(); - POSIX::close(0) or warn "writer exited $?\n"; - POSIX::dup2($stdin, 0) or die "couldn't dup2: $!\n"; - waitpid($pid, 0); - } else { - POSIX::close($r) or die "couldn't close: $!\n"; - #- don't screw up reader - POSIX::dup2(POSIX::open('/dev/null', &POSIX::O_WRONLY), 2) or die "couldn't dup2: $!\n"; - POSIX::dup2($w, 1) or die "couldn't dup2: $!\n"; - POSIX::close($w); - $| = 1; #- autoflush write - exit !$writer->(); - } -} - -sub record_onthefly { - my ($live, $opts) = @_; - - my $record = $live->{media}->get_storage_setting('record'); - unless ($record) { - warn "not implemented yet\n"; - return; - } - if (my $create = $live->{media}->get_storage_setting('record_needs_master') && $live->{media}->get_storage_setting('create')) { - local $opts->{onthefly} = 1; - #- pipe creation step to recording step - pipe_subs(sub { $create->($live, $opts) }, - sub { $record->($live, $opts) }); - } else { - #- no creation step, record directly - $record->($live, $opts); - } -} - -sub need_compressed_image { - my ($live) = @_; - #- compress image if not having loopbacks already - !to_bool(list_selected_loopbacks($live)); -} - -sub get_disk_image_path { - my ($live) = @_; - if ($live->{settings}{compression_method} eq 'gzip') { - get_disk_master_path($live) . if_(need_compressed_image($live), '.gz'); - } - else { - get_disk_master_path($live) . if_(need_compressed_image($live), '.bz2'); - } -} - -sub create_disk_image { - my ($live) = @_; - if (!need_compressed_image($live)) { - warn "already using loopback: skipping image creation\n"; - return; - } - my $master = get_disk_master_path($live); - my $dest = get_disk_image_path($live); - mkdir_p(dirname($dest)); - - if ($live->{settings}{compression_method} eq 'gzip') { - run_('gzip', '>', $dest, '-f', '-c', '--fast', $master); - } - else { - run_('bzip2', '>', $dest, '-f', '-k', '-c', $master); - } -} - -sub create_image { - my ($live) = @_; - - if (my $create = $live->{media}->get_media_setting('image')) { - $create->($live); - } else { - warn "not implemented yet\n"; - } -} - -sub create_vm_image { - my ($live) = @_; - - my $vm_type = $live->{settings}{vm_type}; - if (!$vm_type) { - warn "no vm_type has been set in settings, skipping"; - return; - } - - if (!$live->{media}->get_media_setting('image')) { - warn "not implemented yet\n"; - return; - } - - my $master = get_disk_master_path($live); - if (!-f $master) { - warn "no master image, skipping\n"; - } - - my $vm_image = get_disk_master_prefix($live) . ".$vm_type"; - run_("qemu-img", "convert", "-O", $vm_type, $master, $vm_image); -} - -sub get_rescue_files { - my ($live, $rescue_opts, $o_extra_files) = @_; - my $media_boot = $live->get_media_prefix('boot'); - my $initrd = $media_boot . $rescue_opts->{media}->get_initrd_path; - my @stage2_files = $live->{settings}{replicator_type} eq 'drakx' ? ( - '/usr/lib/drakx-installer-stage2/install/stage2/mdkinst.sqfs', - $live->get_system_root . '/bin/dd', - ) : '/usr/lib/drakx-installer-rescue/rescue.sqfs'; - ( - '/usr/lib/drakx-installer-images/isolinux/alt0/vmlinuz' => $media_boot . '/vmlinuz', - '/usr/lib/drakx-installer-images/isolinux/alt0/all.rdz' => $initrd, - (map { $_ => '/install/stage2/' } @stage2_files), - @{$o_extra_files || []}, - #- FIXME: factorize with copy_files_to to handle glob - (map { $live->{settings}{config_root} . '/' . $_->[0] => '/' . $_->[1] } @{$rescue_opts->{files} || []}), - ); -} - -sub record_rescue_files { - my ($mnt, $device, $rescue_files) = @_; - my $failed; - foreach (group_by2(@$rescue_files)) { - my ($src, $dest) = @$_; - $dest = $mnt . $dest; - mkdir_p($dest =~ m!/$! ? $dest : dirname($dest)); - if (!run_('rsync', '-vdP', '--inplace', $src, $dest)) { - $failed = 1; - last; - } - } - - #- FIXME - chmod 0755, $mnt . '/' . 'oem-rescue.sh'; - - if ($failed) { - run_('umount', $mnt); - maybe_umount_device($device); - die "unable to copy rescue files\n"; - } -} - -sub record_oem_rescue { - my ($live, $opts) = @_; - - my $media = $live->{media}; - my $oem_rescue_idx = $media->find_partition_index('OEM_RESCUE'); - defined $oem_rescue_idx or die "no OEM_RESCUE partition"; - - my $main_device = get_media_device($live, $opts) - or die "unable to find recording device (missing label? try with --device <device>)\n"; - - my $hd = get_hd_from_file($media, $main_device); - supplement_media_partitions($media, $hd); - - my $oem_rescue = $media->{partitions}[$oem_rescue_idx]; - set_part_real_device($hd, $oem_rescue); - - mkdir_p($live->{mnt}); - run_('mount', $oem_rescue->{real_device}, $live->{mnt}) - or die "unable to mount $oem_rescue->{real_device}\n"; - - record_rescue_files($live->{mnt}, $oem_rescue->{real_device}, [ get_rescue_files($live, $live->{oem_rescue}) ]); - - run_('umount', $oem_rescue->{real_device}); - maybe_umount_device($oem_rescue->{real_device}); - devices::del_loop($oem_rescue->{real_device}) if -f $hd->{file}; -} - -sub get_disk_replicator_prefix { - my ($live) = @_; - $live->get_builddir . $live->{prefix}{build}{dist} . '/' . $live->get_name . '.rest'; -} - -sub get_disk_replicator_path { - my ($live) = @_; - get_disk_replicator_prefix($live) . ".img"; -} - -sub get_disk_replicator_files { - my ($live) = @_; - get_rescue_files($live, $live->{replicator}, [ get_disk_image_path($live) => '/images/' ]); -} - -sub get_disk_replicator_images_list { - my ($live) = @_; - my $master_path = get_disk_master_path($live); - my $master_size = (stat($master_path))[7]; - "EN,English," . basename(get_disk_image_path($live)) . ",on,$master_size\n"; -} - -sub create_usb_replicator { - my ($live, $opts) = @_; - my %files = get_disk_replicator_files($live); - local $opts->{slash_size} = fold_left { $::a + $::b } map { directory_usage($_, 'apparent') } keys(%files); - local $opts->{device} = get_disk_replicator_path($live); - allocate_master($live->{replicator}{media}, $opts); - format_master($live->{replicator}{media}, $opts); - record_usb_replicator($live, $opts); -} - -sub record_usb_replicator { - my ($live, $opts) = @_; - - my $media = $live->{replicator}{media}; - my $media_boot = $live->get_media_prefix('boot'); - - $opts->{append} ||= $live->{replicator}{append}; - my $main_device = get_media_device($live, $opts) - or die "unable to find recording device (missing label? try with --device <device>)\n"; - - my $hd = get_hd_from_file($media, $main_device); - supplement_media_partitions($media, $hd); - - my $slash_idx = $media->find_partition_index('/'); - my $slash = $media->{partitions}[$slash_idx]; - set_part_real_device($hd, $slash); - - mkdir_p($live->{mnt}); - run_('mount', $slash->{real_device}, $live->{mnt}) - or die "unable to mount $slash->{real_device}\n"; - rm_rf($live->{mnt} . $media_boot) if -e $live->{mnt} . $media_boot; - { - local $opts->{force_bootloader_config} = 1; - install_disk_bootloader($live, $media, $slash->{real_device}, $opts); - } - - record_rescue_files($live->{mnt}, $slash->{real_device}, [ get_disk_replicator_files($live) ]); - output_p($live->{mnt} . "/images/list", get_disk_replicator_images_list($live)); - - run_('umount', $slash->{real_device}); - maybe_umount_device($slash->{real_device}); - devices::del_loop($slash->{real_device}) if -f $hd->{file}; -} - -sub create_cdrom_replicator { - my ($live, $opts) = @_; - - # FIXME: factorize with usb replicator, write in builddir/replicator/images-list - my $images_list = "/tmp/images-replicator.list"; - output_p($images_list, get_disk_replicator_images_list($live)); - - # FIXME: write in builddir/replicator/syslinux.cfg - my $syslinux_cfg = "/tmp/syslinux-replicator.cfg"; - local $opts->{append} = $live->{replicator}{append}; - output_p($syslinux_cfg, build_syslinux_cfg($live, $live->{replicator}{media}, $opts)); - - my $label = $live->{replicator}{media}->get_media_label or die "the source device must be described by a label\n"; - my $dest = get_cdrom_replicator_path($live); - build_iso_image( - $live, - $dest, - $syslinux_cfg, - $label, - $live->get_media_prefix('boot', $opts->{boot}) . '/syslinux=' . $live->get_builddir . $live->{prefix}{build}{boot} . '/syslinux', - "/images/list=$images_list", - (map { - my ($src, $dest) = @$_; - my $dest_file = $dest =~ m!/$! ? $dest . basename($src) : $dest; - $dest_file . "=" . $src; - } group_by2(get_disk_replicator_files($live))), - ); -} - -sub create_replicator { - my ($live, $opts) = @_; - - if (my $create = $live->{replicator}{media} && $live->{replicator}{media}->get_media_setting('replicator')) { - $create->($live, $opts); - } else { - warn "not implemented yet\n"; - } -} - -sub update_progress_rsync { - my ($live, $total) = @_; - my $all_files = 0; - my $current_file = 0; - $live->{update_progress} ? sub { - if (/^\s*(\d+)\s+\d+%\s+/) { - $current_file = $1; - $live->{update_progress}->(undef, $all_files + $current_file, $total); - } - if (/(?:^|\n)\S+/) { - $all_files += $current_file; - $current_file = 0; - } - } : sub { print }; -} - -sub copy_wizard { - my ($live) = @_; - - MDV::Draklive::Config::complete_config($live); - $live->{system}{vga_mode} = 788 if !defined $live->{system}{vga_mode}; - my $live_media = $live->{prefix}{live}{mnt} . $live->{prefix}{media}{mnt}; - - require interactive; - require wizards; - my $in = 'interactive'->vnew('su'); - my (@available_storage, @available_devices); - my ($storage, $device, $format); - my %source_types = ( - live => N("Use current live system"), - file => N("Select a file"), - ); - my ($source_type, $source_path); - my $media_boot = $live->get_media_prefix('boot'); - my $media_loopbacks = $live->get_media_prefix('loopbacks'); - my $source_is_mounted = sub { -d ($live->{settings}{builddir} . $media_boot) }; - my $umount_source = sub { - if ($source_type ne 'live' && $source_is_mounted->()) { - run_('umount', $live->{settings}{builddir}); - rmdir($live->{settings}{builddir}); - } - }; - my $w; - $w = wizards->new({ - name => N("Live system copy wizard"), - pages => { - welcome => { - name => N("Welcome to the live system copy wizard"), - no_back => 1, - next => 'source', - }, - source => { - name => N("Which live system do you want to copy?"), - data => [ if_(-d ($live_media . $media_loopbacks), - { type => 'list', val => \$source_type, - list => sort(keys(%source_types)), - format => sub { $source_types{$_[0]} } }), - { type => 'file', val => \$source_path, - disabled => sub { $source_type eq 'live' } } ], - pre => $umount_source, - complete => sub { - if ($source_type eq 'live') { - $live->{settings}{builddir} = $live_media; - } else { - $live->{settings}{builddir} = File::Temp::tempdir(); - if (!run_('mount', '-o', 'loop', $source_path, $live->{settings}{builddir})) { - $in->ask_warn(N("Error"), N("Unable to use selected file")); - return 1; - } - } - 0; - }, - post => sub { - my $boot = $live->{settings}{builddir} . $media_boot; - @available_storage = sort(grep { -d "$boot/$_" && exists $MDV::Draklive::Storage::storage_types{$_}{detect} } all($boot)); - if (@available_storage == 1) { - $storage = $available_storage[0]; - return 'device'; - } - return 'storage'; - } - }, - storage => { - name => N("Please select the medium type"), - data => [ { type => 'list', allow_empty_list => 1, - val => \$storage, list => \@available_storage } ], - next => 'device', - }, - device => { - name => N("Please select the device that will contain the new live system"), - pre => sub { - my %devices = map { $_->{device} => $_ } $MDV::Draklive::Storage::storage_types{$storage}{detect}->(); - $_->{formatted_name} = $_->{usb_description} || $_->{info} || $_->{device} foreach values %devices; - @available_devices = (); - require fs::proc_partitions; - foreach (fs::proc_partitions::read([ values %devices ])) { - if ($_->{rootDevice} && exists $devices{$_->{rootDevice}}) { - my $description = $devices{$_->{rootDevice}}{usb_description} || $devices{$_->{rootDevice}}{info}; - $_->{formatted_name} = $description ? "$description ($_->{device})" : $_->{device}; - push @available_devices, $_; - } - } - delete $devices{$_->{rootDevice}} foreach @available_devices; - unshift @available_devices, map { $devices{$_} } sort keys %devices; - undef $device; - }, - data => [ { type => 'list', allow_empty_list => 1, - val => \$device, , list => \@available_devices, - format => sub { $_[0]{formatted_name} } }, - { text => N("Format selected device"), val => \$format, type => 'bool' } ], - complete => sub { - return 0 if defined $device; - $in->ask_warn(N("Error"), N("You must select a device!")); - 1; - }, - post => sub { - (my $_wait, $live->{update_progress}) = $in->wait_message_with_progress_bar; - do { - local $::isInstall = 1; # quick hack to embed the wait message - $live->{update_progress}->(N("Copying in progress")); - }; - eval { - my $opts = { media => { storage => $storage, device => '/dev/' . $device->{device} } }; - format_device($live, $opts) if $format; - record_onthefly($live, $opts); - }; - delete $live->{update_progress}; - if (my $error = $@) { - $in->ask_warn(N("Error"), $error); - $w->{pages}{device}{end} = 1; - } - return "end"; - }, - }, - end => { - name => N("Congratulations") . "\n\n" . N("Your live system is now copied."), - no_back => 1, - end => 1, - }, - } - }); - $w->process($in); - $umount_source->(); - $in->exit; -} - -sub clean { - my ($live) = @_; - # umount filesystem in the live before cleaning - umount_external_fs($live); - umount_system_fs($live); - rm_rf($_) foreach grep { -e $_ } $live->get_builddir, $live->get_system_root; -} - -my @actions = ( - { name => 'dump-config', do => \&MDV::Draklive::Config::dump_config }, - { name => 'clean', do => \&clean }, - { name => 'install', do => \&install_system }, - { name => 'post-install', do => \&post_install_system }, - { name => 'initrd', do => \&create_initrd }, - { name => 'boot', do => \&prepare_bootloader }, - { name => 'tarball', do => \&create_tarball }, - { name => 'loop', do => \&create_loopback_files }, - { name => 'master', do => \&create_master }, - { name => 'image', do => \&create_image }, - { name => 'vm-image', do => \&create_vm_image }, - { name => 'replicator', do => \&create_replicator }, - { name => 'format', do => \&format_device }, - { name => 'record', do => \&record_master }, - { name => 'record-onthefly', do => \&record_onthefly }, - { name => 'record-replicator', do => \&record_replicator }, -); -my @all = qw(install boot loop master image vm-image replicator); - -die "you must be root to run this program\n" if $>; - -my $live_object = 'MDV::Draklive::Live'->new; -my %opts; -my $config_root = $MDV::Draklive::Config::default_config_root; -my $config_path = $MDV::Draklive::Config::default_config_path; -my $settings_path = $MDV::Draklive::Config::default_settings_path; -GetOptions( - "help" => sub { Pod::Usage::pod2usage('-verbose' => 1) }, - "copy-wizard" => \$live_object->{copy_wizard}, - "keep-files" => \$opts{keep_files}, - "boot-only" => \$opts{boot_only}, - "boot-image=s" => sub { $opts{boot} = $_[1]; $opts{boot_only} = 1 }, - "all" => sub { $_->{to_run} = 1 foreach grep { member($_->{name}, @all) } @actions }, - (map { $_->{name} => \$_->{to_run} } @actions), - "device=s" => sub { $opts{device} = $_[1] }, - "all-regions" => sub { $live_object->{all_regions} = 1 }, - "config-root=s" => \$config_root, - "config=s" => \$config_path, - "settings=s" => \$settings_path, - "define=s" => \%{$live_object->{settings}}, -) or Pod::Usage::pod2usage(); - -require standalone; -if ($live_object->{copy_wizard}) { - copy_wizard($live_object); -} else { - every { !$_->{to_run} } @actions and Pod::Usage::pod2usage(); - MDV::Draklive::Config::read_config($live_object, $config_root, $config_path, $settings_path); - MDV::Draklive::Config::check_config($live_object); - MDV::Draklive::Config::complete_config($live_object); - foreach my $region ($live_object->{all_regions} ? sort(keys %{$live_object->{regions}}) : $live_object->{settings}{region}) { - $region and print qq(=== proceeding with region "$region"\n); - $live_object->{settings}{region} = $region; - foreach (grep { $_->{to_run} } @actions) { - print qq(* entering step "$_->{name}"\n); - $_->{do}->($live_object, \%opts); - print qq(* step "$_->{name}" done\n); - } - } -} - -__END__ - -=head1 NAME - -draklive - A live distribution mastering tool - -=head1 SYNOPSIS - -draklive [options] - - Options: - --help long help message - - --install install selected distribution in chroot - --boot prepare initrd and bootloader files - --tarball build chroot tarball - --loop build compressed loopback files - --master build master image - --image build compressed master image - --replicator build replicator image (to dump master on systems) - - --all run all steps, from installation to mastering - - --clean clean installation chroot and work directory - - --device <dev> use this device for live recording (not needed - if the device already has the required label) - --format format selected device - --record record live on selected media - --record-onthefly record live by creating master from loopback files - on the fly - --keep-files keep existing files on media when recording - --record-replicator - record replicator on selected media - - --initrd build initrd only - --post-install run post install only (rpms and patches installation) - - --config-root <dir> - root directory containing config files and additionnal files - defaults to current directory if it contains a configuration file - else, "/etc/draklive" is used - - --config <file> use this configuration file as live description - defaults to "config/live.cfg" - - --settings <file> use this file as live settings (key=value format) - defaults to "config/settings.cfg" - --define key=value - set setting "key" to "value" - takes precedence over values from a settings file - - --all-regions proceed with all configured regions - - --copy-wizard run the copy wizard - - --boot-only copy only boot files - (affects master/record steps) - - --boot-image <method> - create a boot image for the selected method - (affects master/record steps, implies --boot-only) - -Examples: - - draklive --clean - - draklive --all - - draklive --record --device /dev/sdb1 - - draklive --config config/live.cfg --install - -=head1 OPTIONS - -=over 8 - -=item B<--config> - -Makes draklive use the next argument as a configuration file. -This file should contain an hash describing the live distribution, -meaning the system (chroot and boot), media (usb, cdrom, nfs), -and mount type (simple R/W union, union with squash files). - -Here's a configuration sample: - - { - settings { - repository => '/mnt/ken/2006.0', - root => '/chroot/live-move', - }, - system => { - kernel => '2.6.12-12mdk-i586-up-1GB', - auto_install => 'config/auto_inst.cfg.pl', - patch_install => 'config/patch-2006-live.pl', - rpmsrate => 'config/rpmsrate', - rpms => [ - 'rpms/unionfs-kernel-2.6.12-12mdk-i586-up-1GB-1.1.1.1.20051124.1mdk-1mdk.i586.rpm' - ], - patches => [ - 'patches/halt.loopfs.patch', - ], - vga_mode => 788, - no_initrd => 0, - }, - media => { - storage => 'cdrom', - }, - mount => $predefined{mounts}{squash_union} - }; - -=item B<--settings> - -Makes draklive load the next argument as a file in key=value format -into the $live->{settings} hash ($live being the global live configuration hash). - -Built-in keys: - arch: build architecture - builddir: directory hosting build files (initrd, loopbacks, images) - chroot: directory hosting chrooted installations - region: use the matching set of langs from the regions configuration hash - repository: path to the Mandriva distribution repository (ftp/http/local) - -Example keys: - desktop - media - product - -=back - -=head1 DESCRIPTION - -B<draklive> builds a live distribution according to a -configuration file, creates a master image, -and optionally installs it on a device. - -See L<http://qa.mandriva.com/twiki/bin/view/Main/DrakLive> - -=head1 AUTHOR - -Olivier Blin <oblin@mandriva.com> - -=cut |