summaryrefslogtreecommitdiffstats
path: root/draklive2
diff options
context:
space:
mode:
authorMartin Whitaker <mageia@martin-whitaker.me.uk>2017-11-23 13:05:01 +0000
committerMartin Whitaker <mageia@martin-whitaker.me.uk>2017-11-23 13:08:45 +0000
commit3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7 (patch)
tree957620446bd9e049af9043c8e8752d2d5ca8aff1 /draklive2
parent0d61c6c559469cf4df58b0f59a78172c72afe878 (diff)
downloaddrakiso-3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7.tar
drakiso-3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7.tar.gz
drakiso-3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7.tar.bz2
drakiso-3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7.tar.xz
drakiso-3f312ebc4b3f0e8bb55f85eef1784c64ebd707f7.zip
Split draklive into two versions.
In case people want to retain the old functionality.
Diffstat (limited to 'draklive2')
-rwxr-xr-xdraklive21853
1 files changed, 1853 insertions, 0 deletions
diff --git a/draklive2 b/draklive2
new file mode 100755
index 0000000..ad0ce5c
--- /dev/null
+++ b/draklive2
@@ -0,0 +1,1853 @@
+#!/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