summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xdraklive2965
-rwxr-xr-xlib/MGA/DrakISO/BuildBoot.pm423
-rwxr-xr-xlib/MGA/DrakISO/BuildISO.pm163
-rwxr-xr-xlib/MGA/DrakISO/BuildLoop.pm116
-rwxr-xr-xlib/MGA/DrakISO/BuildRoot.pm259
-rw-r--r--lib/MGA/DrakISO/Live.pm17
-rw-r--r--lib/MGA/DrakISO/Utils.pm20
7 files changed, 1055 insertions, 908 deletions
diff --git a/draklive2 b/draklive2
index 4e90123..1fac358 100755
--- a/draklive2
+++ b/draklive2
@@ -20,906 +20,45 @@
# 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 Cwd;
-use File::Temp;
-use File::Copy qw(mv);
-use IO::Handle; #- autoflush
-use MGA::DrakISO::Utils;
-use MGA::DrakISO::Live;
-use MGA::DrakISO::Loopback;
-use MGA::DrakISO::Config;
-use MGA::DrakISO::Storage;
-
-###############################################################################
-# Common Helper Functions
-###############################################################################
-
-sub get_langs {
- my ($live) = @_;
- uniq(
- (ref $live->{regions} ? @{$live->{regions}{$live->{settings}{region}}} : ()),
- @{$live->{system}{langs_always}}
- );
-}
-
-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');
-}
-
-sub umount_external_fs {
- my ($live) = @_;
- my $system_root = Cwd::abs_path($live->get_system_root);
- my @mounts = grep { $_ =~ $system_root } split("\n", cat_('/proc/mounts'));
- foreach (reverse(@mounts)) {
- my @field = split(' ' , $_);
- fs::mount::umount($field[1]);
- }
-}
-
-sub get_absolute_path {
- my ($live, $path) = @_;
- if (defined $path && substr($path, 0, 1) ne '/') {
- $live->{settings}{config_root} . '/' . $path;
- } else {
- $path;
- }
-}
-
-###############################################################################
-# Install Phase
-###############################################################################
-
-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 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_external_fs($live);
-
- umask $previous_umask;
-}
-
-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 = MGA::DrakISO::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 remove_files_from {
- my ($files, $root) = @_;
- run_('find', $root, '(', join_lists('-o', map { [ '-name', $_ ] } @$files), ')', '-exec', 'rm', '-r', '{}', ';')
- if $files && @$files;
-}
-
-sub join_lists {
- my ($separator, $head, @lists) = @_;
- @{$head || []}, map { $separator, @$_ } @lists;
-}
-
-sub clean_system_conf_file {
- my ($live, $file) = @_;
- substInFile { undef $_ if /^[^#]/ } $live->get_system_root . $file;
-}
-
-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');
+use MGA::DrakISO::Config;
+use MGA::DrakISO::Live;
+use MGA::DrakISO::Utils;
- 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));
-}
+use MGA::DrakISO::BuildRoot;
+use MGA::DrakISO::BuildBoot;
+use MGA::DrakISO::BuildLoop;
+use MGA::DrakISO::BuildISO;
###############################################################################
-# Loop Phase
+# Actions
###############################################################################
-sub create_loopback_files {
+sub clean {
my ($live) = @_;
- # make sure no external filesystems are mounted before creating the loopback
+ # make sure no external filesystems are mounted before cleaning
umount_external_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 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 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);
-}
-
-###############################################################################
-# Boot Phase
-###############################################################################
-
-sub prepare_bootloader {
- my ($live) = @_;
- create_initrd($live);
- create_bootloader($live);
-}
-
-sub create_initrd {
- my ($live) = @_;
- my $root = $live->get_system_root;
-
- my $kernel = $live->find_kernel;
- print "using kernel $kernel->{version}\n";
-
- my $initrd = $root . '/boot/' . $live->get_initrd_name;
- unlink($initrd);
-
- {
- my $bootloader = {};
- local $::prefix = $root;
- bootloader::add_kernel($bootloader, $kernel, { label => 'linux', vga => $live->{system}{vga_mode} }, '', $live->{system}{no_initrd});
- }
-
- my $boot_dir = $live->get_builddir . $live->{prefix}{build}{boot};
- mkdir_p($boot_dir);
- mv($initrd, $boot_dir . '/initrd.gz') or die "cannot move initrd: $!\n";
-}
-
-sub create_bootloader {
- my ($live) = @_;
-
- # Create a build directory. This will contain all the files we need to
- # exist in /boot on the ISO.
- my $boot_dir = $live->get_builddir . $live->{prefix}{build}{boot};
- mkdir_p($boot_dir);
-
- # Locate and copy the kernel we want to boot.
- my $kernel = $live->find_kernel->{version};
- my $vmlinuz = $live->get_system_root . '/boot/vmlinuz-' . $kernel;
- -e $vmlinuz or die "cannot find kernel $kernel in root system\n";
- cp_f($vmlinuz, $boot_dir . '/vmlinuz');
-
- # Create a subdirectory to hold the grub2 bootloader.
- my $grub2_dir = $boot_dir . '/grub2';
- mkdir_p($grub2_dir);
-
- # Locate and copy the default font for the bootloader. If we can't find a
- # font, don't worry - the bootloader will fall back to text mode.
- my $font = get_absolute_path($live, $live->{media}{bootloader_font});
- if (defined $font) {
- -e $font or die "cannot find bootloader font file $font\n";
- } else {
- $font = '/usr/share/grub/unicode.pf2';
- }
- if (-e $font) {
- my $fonts_dir = $grub2_dir . '/fonts';
- mkdir_p($fonts_dir);
- cp_f($font, $fonts_dir);
- }
-
- # Locate and copy the bootloader theme. Default to the standard Mageia
- # theme if the user hasn't specified one. If that's not available either,
- # proceed without a theme.
- my $theme = get_absolute_path($live, $live->{media}{bootloader_theme});
- if (defined $theme) {
- -d $theme or die "cannot find bootloader theme directory $theme\n";
- } else {
- $theme = '/boot/grub2/themes/maggy';
- }
- my $theme_name = basename($theme);
- my @theme_fonts;
- if (-d $theme) {
- my $themes_dir = $grub2_dir . '/themes';
- mkdir_p($themes_dir);
- cp_f($theme, $themes_dir);
- @theme_fonts = map { basename($_) } glob("$theme/*.pf2");
- }
-
- # If the user has provided the necessary configuration data, construct
- # the bootloader language and keyboard selection submenus and copy the
- # grub2 keyboard layout files.
- my $add_lang_menu = defined $live->{media}{bootloader_langs};
- my $add_kbd_menu = defined $live->{media}{bootloader_kbds};
- if ($add_lang_menu) {
- my $lang_names = get_absolute_path($live, $live->{media}{bootloader_langs});
- -e $lang_names or die "cannot find bootloader language name file $lang_names\n";
- my @langs = group_by2(eval(cat_($lang_names))) or die "error in language name file $lang_names\n";
-
- my $lang_kbds = dirname($lang_names) . '/lang-kbds.txt';
- my $kbds;
- if ($add_kbd_menu && -e $lang_kbds) {
- $kbds = eval(cat_($lang_kbds)) or die "error in language keyboard file $lang_kbds\n";
- }
-
- MDK::Common::File::output_utf8($grub2_dir . '/lang-menu.cfg', build_lang_menu_cfg(\@langs, %$kbds));
- }
- if ($add_kbd_menu) {
- my $kbd_names = get_absolute_path($live, $live->{media}{bootloader_kbds});
- -e $kbd_names or die "cannot find bootloader keyboard name file $kbd_names\n";
- my @kbds = group_by2(eval(cat_($kbd_names))) or die "error in keyboard name file $kbd_names\n";
-
- my $layouts = dirname($kbd_names) . '/layouts';
- -d $layouts or die "cannot find bootloader keyboard map directory $layouts\n";
- cp_f($layouts, $grub2_dir);
-
- MDK::Common::File::output_utf8($grub2_dir . '/kbd-menu.cfg', build_kbd_menu_cfg(\@kbds));
- }
-
- # Copy any message translation files the user has provided.
- my $messages = get_absolute_path($live, $live->{media}{bootloader_messages});
- if (defined $messages) {
- -d $messages or die "cannot find bootloader messages directory $messages\n";
- my $locale_dir = $grub2_dir . '/locale';
- mkdir_p($locale_dir);
- cp_f(glob($messages . '/*.mo'), $locale_dir);
- }
-
- # If the user has supplied a grub2 image for non-UEFI boot, copy that,
- # otherwise build one.
- my $eltorito_img = get_absolute_path($live, $live->{media}{eltorito_img});
- if (defined $eltorito_img) {
- -e $eltorito_img or die "cannot find El Torito boot image $eltorito_img\n";
- cp_f($eltorito_img, $grub2_dir . '/eltorito.img');
- } else {
- build_grub2_eltorito_img($live, $grub2_dir . '/eltorito.img');
- }
-
- my $label = $live->{media}->get_media_label;
-
- # If the user has supplied a top-level grub2 configuration file, copy that
- # (replacing the "VOLUME_LABEL" template with the actual label for the ISO
- # image), otherwise build one.
- my $grub2_cfg = $grub2_dir . '/grub.cfg';
- if (defined $live->{media}{grub2_cfg}) {
- my $grub_cfg_template = get_absolute_path($live, $live->{media}{grub2_cfg});
- -e $grub_cfg_template or die "cannot find grub2 config file $grub_cfg_template\n";
- cp_f($grub_cfg_template, $grub2_cfg);
- run_("sed", "-i", "s/VOLUME_LABEL/$label/g", $grub2_cfg);
- } else {
- output($grub2_cfg, build_grub2_cfg($live, $theme_name, \@theme_fonts, $add_lang_menu, $add_kbd_menu));
- }
-
- my $title = $label =~ s/-/ /gr;
-
- # If we have a theme, replace the menu title with the name of the ISO
- # (extracted from the disk label).
- my $base_theme_txt = $grub2_dir . "/themes/$theme_name/theme.txt";
- if (-e $base_theme_txt) {
- run_('sed', '-i', qq(s/title-text:.*/title-text: "$title"/), $base_theme_txt);
- }
-
- # If we are building a 32-bit ISO, we are done, as we don't support
- # 32-bit UEFI boot.
- return if $live->{settings}{arch} ne 'x86_64';
-
- # Create another build directory. This will contain all the files we need
- # to exist in the /EFI directory on the ISO.
- my $efi_root_dir = $live->get_builddir . $live->{prefix}{build}{EFI};
- my $efi_boot_dir = $efi_root_dir . '/BOOT';
- mkdir_p($efi_boot_dir);
-
- # If the user has supplied a grub2 image for UEFI boot, copy that,
- # otherwise build one.
- my $bootx64_efi = get_absolute_path($live, $live->{media}{bootx64_efi});
- if (defined $bootx64_efi) {
- -e $bootx64_efi or die "cannot find EFI boot image $bootx64_efi\n";
- cp_f($bootx64_efi, $efi_boot_dir . '/bootx64.efi');
- } else {
- build_grub2_bootx64_efi($live, $efi_boot_dir . '/bootx64.efi');
- }
-
- # Build a grub2 configuration file for UEFI boot. This just chains to the
- # main grub2 configuration file.
- output($efi_boot_dir . '/grub.cfg', build_uefi_grub2_cfg($live));
-
- # If we have a theme, duplicate the theme configuration file and modify the
- # title string to indicate we are doing a UEFI boot. This is useful when
- # dealing with user bug reports...
- if (-e $base_theme_txt) {
- my $uefi_theme_txt = $grub2_dir . "/themes/$theme_name/theme-uefi.txt";
- cp_f($base_theme_txt, $uefi_theme_txt);
- run_('sed', '-i', qq(s/title-text:.*/title-text: "$title (UEFI)"/), $uefi_theme_txt);
- }
-
- # Create another build directory for temporarily storing the ESP image.
- my $images_dir = $live->get_builddir . $live->{prefix}{build}{images};
- mkdir_p($images_dir);
-
- # Construct an ESP image. This is needed for USB boot.
- my $esp_image = $images_dir . '/esp.img';
- eval { rm_rf($esp_image) };
- run_("/sbin/mkdosfs", "-F12", "-C", $esp_image, "4096");
- run_("mcopy", "-s", "-i", $esp_image, $efi_root_dir, "::");
-}
-
-sub build_grub2_eltorito_img {
- my ($live, $output) = @_;
-
- my @modules = qw(biosdisk iso9660 fat part_msdos all_video font png gfxterm gfxmenu linux
- keylayouts at_keyboard usb_keyboard configfile echo gettext ls search test);
-
- run_('grub2-mkimage',
- '--output', $output,
- '--prefix', $live->get_media_prefix('boot') . '/grub2',
- '--format', 'i386-pc-eltorito',
- @modules
- );
-}
-
-sub build_grub2_bootx64_efi {
- my ($live, $output) = @_;
-
- my @modules = qw(iso9660 fat part_msdos all_video font png gfxterm gfxmenu linux
- keylayouts at_keyboard usb_keyboard configfile echo gettext ls search test);
-
- run_('grub2-mkimage',
- '--output', $output,
- '--prefix', $live->get_media_prefix('EFI') . '/BOOT',
- '--format', 'x86_64-efi',
- @modules
- );
-}
-
-sub build_grub2_cfg {
- my ($live, $theme_name, $theme_fonts, $add_lang_menu, $add_kbd_menu) = @_;
-
- my @loadfonts;
- if (defined $theme_name) {
- @loadfonts = map { " loadfont \$prefix/themes/$theme_name/$_" } @$theme_fonts;
- }
-
- my $gettext = $add_lang_menu ? '$' : '';
-
- my $boot_dir = $live->get_media_prefix('boot');
-
- join("\n",
- "if [ -z \$initialised ] ; then",
- " search --no-floppy --set=root -l '" . $live->{media}->get_media_label . "'",
- " set prefix=(\$root)" . $live->get_media_prefix('boot') . "/grub2",
- "",
- " if loadfont \$prefix/fonts/unicode.pf2 ; then",
- " set gfxmode=1024x768,800x600,auto",
- " set gfxpayload=keep",
- " terminal_output gfxterm",
- " fi",
- if_($theme_name,
- "",
- " if [ x\$uefi == 'xtrue' ] ; then",
- " set theme=\$prefix/themes/$theme_name/theme-uefi.txt",
- " else",
- " set theme=\$prefix/themes/$theme_name/theme.txt",
- " fi",
- " export theme",
- @loadfonts,
- ),
- " set initialised=true",
- " export initialised",
- "fi",
- "",
- "set default=" . get_bootloader_default($live),
- "set timeout=" . get_bootloader_timeout($live),
- "",
- if_($add_lang_menu,
- "export lang",
- "export lkbd",
- "",
- ),
- if_($add_kbd_menu,
- "export kbd",
- "",
- ),
- (map {
- my ($name, $cmdline) = @$_;
- join("\n",
- "menuentry $gettext\"$name\" {",
- " linux $boot_dir/vmlinuz " . get_default_append($live) . if_($cmdline, " $cmdline"),
- " initrd $boot_dir/initrd.gz",
- "}"
- );
- } group_by2(@{$live->{media}{bootloader_entries}})),
- if_($add_lang_menu || $add_kbd_menu,
- # this acts as a spacer
- "menuentry '________________________' {",
- " set dummy=true",
- "}",
- ),
- if_($add_lang_menu,
- "submenu \"F2: \"$gettext\"Language [\$lang]\" --id language --hotkey f2 {",
- " source \$prefix/lang-menu.cfg",
- "}",
- ),
- if_($add_kbd_menu,
- "submenu \"F3: \"$gettext\"Keyboard [\$kbd]\" --id keyboard --hotkey f3 {",
- " source \$prefix/kbd-menu.cfg",
- "}",
- ),
- "",
- );
-}
-
-sub get_bootloader_default {
- my ($live) = @_;
- defined $live->{media}{bootloader_default} ? $live->{media}{bootloader_default} : 0;
-}
-
-sub get_bootloader_timeout {
- my ($live) = @_;
- defined $live->{media}{bootloader_timeout} ? $live->{media}{bootloader_timeout} : 4;
-}
-
-sub get_default_append {
- my ($live) = @_;
- my $append = $live->{system}{append};
- join(" ",
- "root=mgalive:LABEL=" . $live->{media}->get_media_label,
- "lang=\$lang kbd=\$kbd",
- if_($append, $append),
- if_($live->{system}{vga_mode} && $append !~ /\bvga=\b/,
- "vga=" . $live->{system}{vga_mode}),
- );
-}
-
-sub build_lang_menu_cfg {
- my ($langs, %kbds) = @_;
- join("\n",
- "function set_language {",
- " set lang=\$1",
- " set lkbd=\$2",
- " configfile \$prefix/grub.cfg",
- "}",
- "",
- "set default=\$lang",
- "set timeout=-1",
- "",
- "menuentry \$\"[more options after boot]\" { set_language '' '' }",
- (map {
- my ($id, $name) = @$_;
- my $kbd = $kbds{$id};
- "menuentry '$name' --id $id { set_language $id $kbd }";
- } (@$langs)),
- "",
- );
-}
-
-sub build_kbd_menu_cfg {
- my ($kbds) = @_;
- join("\n",
- "function set_keyboard {",
- " if [ -z \$kbd ] ; then",
- " terminal_input at_keyboard",
- " fi",
- " set kbd=\$1",
- " set lkbd=",
- " keymap \$prefix/layouts/\$kbd.gkb",
- " configfile \$prefix/grub.cfg",
- "}",
- "",
- "if [ -z \$kbd ] ; then",
- " set default=\$lkbd",
- "else",
- " set default=\$kbd",
- "fi",
- "",
- "set timeout=-1",
- "",
- (map {
- my ($id, $name) = @$_;
- $name =~ s/"/\\"/g;
- "menuentry \$\"$name\" --id $id { set_keyboard $id }";
- } (@$kbds)),
- "",
- );
-}
-
-sub build_uefi_grub2_cfg {
- my ($live) = @_;
- join("\n",
- "search --no-floppy --set=root -l '" . $live->{media}->get_media_label . "'",
- "set prefix=(\$root)" . $live->get_media_prefix('boot') . "/grub2",
- "",
- "set uefi=true",
- "export uefi",
- "",
- "configfile \$prefix/grub.cfg",
- "",
- );
-}
-
-###############################################################################
-# Master Phase
-###############################################################################
-
-sub create_master {
- my ($live) = @_;
-
- if (my $create = $live->{media}->get_storage_setting('create')) {
- $create->($live);
- } else {
- warn "not implemented yet\n";
- }
+ rm_rf($_) foreach grep { -e $_ } $live->get_builddir, $live->get_system_root;
}
-sub create_iso_master {
+sub prepare_root {
my ($live) = @_;
-
- my $label = $live->{media}->get_media_label or die "the source device must be described by a label\n";
-
- my $mbr_image = get_absolute_path($live, $live->{media}{mbr_boot_img}) // '/usr/lib/grub/i386-pc/boot_hybrid.img';
- -e $mbr_image or die "cannot find MBR boot image $mbr_image\n";
-
- my $esp_image = $live->get_builddir . $live->{prefix}{build}{images} . '/esp.img';
- -e $esp_image or die "cannot find ESP image $esp_image\n";
-
- my $dest = $live->get_builddir . $live->{prefix}{build}{dist} . '/' . $live->get_name . '.iso';
- mkdir_p(dirname($dest));
-
- build_iso_image(
- $live,
- $dest,
- $label,
- $mbr_image,
- $esp_image,
- $live->get_media_prefix('boot') . '=' . $live->get_builddir . $live->{prefix}{build}{boot},
- if_($live->{settings}{arch} eq 'x86_64',
- $live->get_media_prefix('EFI') . '=' . $live->get_builddir . $live->{prefix}{build}{EFI},
- ),
- (
- map {
- $live->get_media_prefix('loopbacks') . $_ .
- '=' .
- $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, $label, $mbr_image, $esp_image, @opts) = @_;
-
- my $boot = $live->get_media_prefix('boot'); # normally '/boot'
- my $EFI = $live->get_media_prefix('EFI'); # normally '/EFI'
-
- # This builds a hybrid ISO capable of both legacy and UEFI boot. The
- # ISO contains a primary iso9660 partition (which is the only thing
- # visible when booting from DVD) and a secondary ESP partition (which
- # is only used for USB UEFI boot).
- #
- # For legacy boot, the system boots the grub2 El Torito image. For DVD
- # boot, this is located via the El Torito catalogue. For USB boot, it
- # is located via the grub2 MBR hybrid boot image, which is stored in
- # the MBR. In both cases the initial grub2 root location is the iso9660
- # partition, so the initial grub2 configuration file will be read from
- # that partition.
- #
- # For UEFI boot, the system boots the grub2 EFI image. For DVD boot,
- # this is located via the El Torito catalogue, and we store it in the
- # iso9660 partition. For USB boot, it must be located in the /EFI/BOOT
- # directory in the ESP. For DVD boot the initial grub2 root location is
- # the iso9660 partition, for USB boot it is the ESP partition, and the
- # initial grub2 configuration file will be located accordingly.
- #
- run_('xorrisofs',
- '-pad', '-l', '-R', '-J',
- '-V', $label,
- '-graft-points',
- '-hide-rr-moved',
- '--sort-weight', 0, '/',
- '--sort-weight', 1, $boot,
- # for hybrid MBR boot
- '--grub2-mbr', $mbr_image,
- '-b', "$boot/grub2/eltorito.img",
- '-no-emul-boot',
- '-boot-load-size', 4,
- '-boot-info-table',
- '--grub2-boot-info',
- if_($live->{settings}{arch} eq 'x86_64',
- # for DVD UEFI boot
- '-eltorito-alt-boot',
- '-e', "$EFI/BOOT/bootx64.efi",
- '-no-emul-boot',
- # for USB UEFI boot
- '-part_like_isohybrid',
- '-iso_mbr_part_type', '0x00',
- '-append_partition', 2, '0xef', $esp_image,
- ),
- if_($dest, '-o', $dest),
- @opts,
- ) or die "unable to run xorrisofs\n";
-
- if ($dest) {
- my $dir = dirname($dest);
- my $filename = basename($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";
- }
- }
- }
+ MGA::DrakISO::BuildRoot::install_live_system($live);
+ MGA::DrakISO::BuildRoot::customise_live_system($live);
}
-###############################################################################
-# Clean Phase
-###############################################################################
-
-sub clean {
+sub prepare_boot {
my ($live) = @_;
- # make sure no external filesystems are mounted before cleaning
- umount_external_fs($live);
- rm_rf($_) foreach grep { -e $_ } $live->get_builddir, $live->get_system_root;
+ MGA::DrakISO::BuildBoot::prepare_live_system_boot($live);
+ MGA::DrakISO::BuildBoot::prepare_iso_bootloader($live);
}
###############################################################################
@@ -927,39 +66,44 @@ sub clean {
###############################################################################
my @actions = (
- { name => 'dump-config', do => \&MGA::DrakISO::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 => 'loop', do => \&create_loopback_files },
- { name => 'master', do => \&create_master },
+ { name => 'dump-config', do => \&MGA::DrakISO::Config::dump_config },
+ { name => 'clean', do => \&clean },
+ { name => 'root', do => \&prepare_root },
+ { name => 'root-create', do => \&MGA::DrakISO::BuildRoot::install_live_system },
+ { name => 'root-customise', do => \&MGA::DrakISO::BuildRoot::customise_live_system },
+ { name => 'boot', do => \&prepare_boot },
+ { name => 'boot-system', do => \&MGA::DrakISO::BuildBoot::prepare_live_system_boot },
+ { name => 'boot-loader', do => \&MGA::DrakISO::BuildBoot::prepare_iso_bootloader },
+ { name => 'loop', do => \&MGA::DrakISO::BuildLoop::build_live_loopback_files },
+ { name => 'master', do => \&MGA::DrakISO::BuildISO::build_live_iso },
);
-my @all = qw(install boot loop master);
+my @all = qw(root boot loop master);
die "you must be root to run this program\n" if $>;
-my $live_object = 'MGA::DrakISO::Live'->new;
-my $config_root = $MGA::DrakISO::Config::default_config_root;
-my $config_path = $MGA::DrakISO::Config::default_config_path;
+my $live_object = 'MGA::DrakISO::Live'->new;
+my $config_root = $MGA::DrakISO::Config::default_config_root;
+my $config_path = $MGA::DrakISO::Config::default_config_path;
my $settings_path = $MGA::DrakISO::Config::default_settings_path;
+
GetOptions(
- "help" => sub { Pod::Usage::pod2usage('-verbose' => 1) },
- "all" => sub { $_->{to_run} = 1 foreach grep { member($_->{name}, @all) } @actions },
- (map { $_->{name} => \$_->{to_run} } @actions),
- "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}},
+ "help" => sub { Pod::Usage::pod2usage('-verbose' => 1) },
+ "all" => sub { $_->{to_run} = 1 foreach grep { member($_->{name}, @all) } @actions },
+ (map { $_->{name} => \$_->{to_run} } @actions),
+ "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;
every { !$_->{to_run} } @actions and Pod::Usage::pod2usage();
+
MGA::DrakISO::Config::read_config($live_object, $config_root, $config_path, $settings_path);
MGA::DrakISO::Config::check_config($live_object);
MGA::DrakISO::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;
@@ -972,6 +116,10 @@ require standalone;
__END__
+###############################################################################
+# Documentation
+###############################################################################
+
=head1 NAME
draklive2 - A live distribution mastering tool
@@ -983,17 +131,20 @@ draklive2 [options]
Options:
--help long help message
- --install install selected distribution in chroot
- --boot prepare initrd and bootloader files
+ --root install and customise Live system in chroot
+ --boot prepare system boot and ISO bootloader files
--loop build compressed loopback files
--master build master image
- --all run all steps, from installation to mastering
+ --all run all the above steps
--clean clean installation chroot and work directory
- --post-install run post install only
- --initrd build initrd only
+ --root-install (for debug) run base install of Live system
+ --root-customise (for debug) run system customisation steps
+
+ --boot-system (for debug) prepare kernel and initrd for system boot
+ --boot-loader (for debug) prepare bootloader for ISO
--config-root <dir>
root directory containing config and additional files
@@ -1018,7 +169,7 @@ Examples:
draklive2 --all
- draklive2 --config config/live.cfg --install
+ draklive2 --config config/live.cfg --root
=head1 OPTIONS
diff --git a/lib/MGA/DrakISO/BuildBoot.pm b/lib/MGA/DrakISO/BuildBoot.pm
new file mode 100755
index 0000000..895f7b7
--- /dev/null
+++ b/lib/MGA/DrakISO/BuildBoot.pm
@@ -0,0 +1,423 @@
+# Copyright (C) 2005 Mandriva
+# Olivier Blin <oblin@mandriva.com>
+# Copyright (C) 2017 Mageia
+# Martin Whitaker <mageia@martin-whitaker.me.uk>
+#
+# 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.
+
+package MGA::DrakISO::BuildBoot;
+
+use strict;
+
+use MDK::Common;
+use common;
+
+use File::Copy qw(mv);
+
+use MGA::DrakISO::Live;
+use MGA::DrakISO::Utils;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(prepare_live_system_boot prepare_iso_bootloader);
+
+###############################################################################
+# Live System
+###############################################################################
+
+sub prepare_live_system_boot {
+ my ($live) = @_;
+
+ # Create a build directory. This will contain all the files we need to
+ # exist in /boot on the ISO.
+ my $boot_dir = $live->get_builddir . $live->{prefix}{build}{boot};
+ mkdir_p($boot_dir);
+
+ # Locate the kernel we want to boot.
+ my $kernel = $live->find_kernel;
+ print "Using kernel $kernel->{version}\n";
+
+ # Copy the kernel into the build directory.
+ my $vmlinuz = $live->get_system_root . '/boot/vmlinuz-' . $kernel->{version};
+ -e $vmlinuz or die "cannot find kernel $kernel->{version} in root system\n";
+ cp_f($vmlinuz, $boot_dir . '/vmlinuz');
+
+ # Build an initrd suitable for Live boot.
+ my $initrd = $live->get_system_root . '/boot/' . $live->get_initrd_name;
+ unlink($initrd);
+ {
+ my $bootloader = {};
+ local $::prefix = $live->get_system_root;
+ bootloader::add_kernel($bootloader, $kernel, { label => 'linux', vga => $live->{system}{vga_mode} }, '', $live->{system}{no_initrd});
+ }
+
+ # Move the initrd into the build directory.
+ mv($initrd, $boot_dir . '/initrd.gz') or die "cannot move initrd: $!\n";
+}
+
+###############################################################################
+# ISO Bootloader
+###############################################################################
+
+sub prepare_iso_bootloader {
+ my ($live) = @_;
+
+ # Create a subdirectory to hold the grub2 bootloader.
+ my $grub2_dir = $live->get_builddir . $live->{prefix}{build}{boot} . '/grub2';
+ mkdir_p($grub2_dir);
+
+ # Locate and copy the default font for the bootloader. If we can't find a
+ # font, don't worry - the bootloader will fall back to text mode.
+ my $font = $live->get_absolute_path($live->{media}{bootloader_font});
+ if (defined $font) {
+ -e $font or die "cannot find bootloader font file $font\n";
+ } else {
+ $font = '/usr/share/grub/unicode.pf2';
+ }
+ if (-e $font) {
+ my $fonts_dir = $grub2_dir . '/fonts';
+ mkdir_p($fonts_dir);
+ cp_f($font, $fonts_dir);
+ }
+
+ # Locate and copy the bootloader theme. Default to the standard Mageia
+ # theme if the user hasn't specified one. If that's not available either,
+ # proceed without a theme.
+ my $theme = $live->get_absolute_path($live->{media}{bootloader_theme});
+ if (defined $theme) {
+ -d $theme or die "cannot find bootloader theme directory $theme\n";
+ } else {
+ $theme = '/boot/grub2/themes/maggy';
+ }
+ my $theme_name = basename($theme);
+ my @theme_fonts;
+ if (-d $theme) {
+ my $themes_dir = $grub2_dir . '/themes';
+ mkdir_p($themes_dir);
+ cp_f($theme, $themes_dir);
+ @theme_fonts = map { basename($_) } glob("$theme/*.pf2");
+ }
+
+ # If the user has provided the necessary configuration data, construct
+ # the bootloader language and keyboard selection submenus and copy the
+ # grub2 keyboard layout files.
+ my $add_lang_menu = defined $live->{media}{bootloader_langs};
+ my $add_kbd_menu = defined $live->{media}{bootloader_kbds};
+ if ($add_lang_menu) {
+ my $lang_names = $live->get_absolute_path($live->{media}{bootloader_langs});
+ -e $lang_names or die "cannot find bootloader language name file $lang_names\n";
+ my @langs = group_by2(eval(cat_($lang_names))) or die "error in language name file $lang_names\n";
+
+ my $lang_kbds = dirname($lang_names) . '/lang-kbds.txt';
+ my $kbds;
+ if ($add_kbd_menu && -e $lang_kbds) {
+ $kbds = eval(cat_($lang_kbds)) or die "error in language keyboard file $lang_kbds\n";
+ }
+
+ MDK::Common::File::output_utf8($grub2_dir . '/lang-menu.cfg', build_lang_menu_cfg(\@langs, %$kbds));
+ }
+ if ($add_kbd_menu) {
+ my $kbd_names = $live->get_absolute_path($live->{media}{bootloader_kbds});
+ -e $kbd_names or die "cannot find bootloader keyboard name file $kbd_names\n";
+ my @kbds = group_by2(eval(cat_($kbd_names))) or die "error in keyboard name file $kbd_names\n";
+
+ my $layouts = dirname($kbd_names) . '/layouts';
+ -d $layouts or die "cannot find bootloader keyboard map directory $layouts\n";
+ cp_f($layouts, $grub2_dir);
+
+ MDK::Common::File::output_utf8($grub2_dir . '/kbd-menu.cfg', build_kbd_menu_cfg(\@kbds));
+ }
+
+ # Copy any message translation files the user has provided.
+ my $messages = $live->get_absolute_path($live->{media}{bootloader_messages});
+ if (defined $messages) {
+ -d $messages or die "cannot find bootloader messages directory $messages\n";
+ my $locale_dir = $grub2_dir . '/locale';
+ mkdir_p($locale_dir);
+ cp_f(glob($messages . '/*.mo'), $locale_dir);
+ }
+
+ # If the user has supplied a grub2 image for non-UEFI boot, copy that,
+ # otherwise build one.
+ my $eltorito_img = $live->get_absolute_path($live->{media}{eltorito_img});
+ if (defined $eltorito_img) {
+ -e $eltorito_img or die "cannot find El Torito boot image $eltorito_img\n";
+ cp_f($eltorito_img, $grub2_dir . '/eltorito.img');
+ } else {
+ build_grub2_eltorito_img($live, $grub2_dir . '/eltorito.img');
+ }
+
+ my $label = $live->{media}->get_media_label;
+
+ # If the user has supplied a top-level grub2 configuration file, copy that
+ # (replacing the "VOLUME_LABEL" template with the actual label for the ISO
+ # image), otherwise build one.
+ my $grub2_cfg = $grub2_dir . '/grub.cfg';
+ if (defined $live->{media}{grub2_cfg}) {
+ my $grub_cfg_template = $live->get_absolute_path($live->{media}{grub2_cfg});
+ -e $grub_cfg_template or die "cannot find grub2 config file $grub_cfg_template\n";
+ cp_f($grub_cfg_template, $grub2_cfg);
+ run_("sed", "-i", "s/VOLUME_LABEL/$label/g", $grub2_cfg);
+ } else {
+ output($grub2_cfg, build_grub2_cfg($live, $theme_name, \@theme_fonts, $add_lang_menu, $add_kbd_menu));
+ }
+
+ my $title = $label =~ s/-/ /gr;
+
+ # If we have a theme, replace the menu title with the name of the ISO
+ # (extracted from the disk label).
+ my $base_theme_txt = $grub2_dir . "/themes/$theme_name/theme.txt";
+ if (-e $base_theme_txt) {
+ run_('sed', '-i', qq(s/title-text:.*/title-text: "$title"/), $base_theme_txt);
+ }
+
+ # If we are building a 32-bit ISO, we are done, as we don't support
+ # 32-bit UEFI boot.
+ return if $live->{settings}{arch} ne 'x86_64';
+
+ # Create another build directory. This will contain all the files we need
+ # to exist in the /EFI directory on the ISO.
+ my $efi_root_dir = $live->get_builddir . $live->{prefix}{build}{EFI};
+ my $efi_boot_dir = $efi_root_dir . '/BOOT';
+ mkdir_p($efi_boot_dir);
+
+ # If the user has supplied a grub2 image for UEFI boot, copy that,
+ # otherwise build one.
+ my $bootx64_efi = $live->get_absolute_path($live->{media}{bootx64_efi});
+ if (defined $bootx64_efi) {
+ -e $bootx64_efi or die "cannot find EFI boot image $bootx64_efi\n";
+ cp_f($bootx64_efi, $efi_boot_dir . '/bootx64.efi');
+ } else {
+ build_grub2_bootx64_efi($live, $efi_boot_dir . '/bootx64.efi');
+ }
+
+ # Build a grub2 configuration file for UEFI boot. This just chains to the
+ # main grub2 configuration file.
+ output($efi_boot_dir . '/grub.cfg', build_uefi_grub2_cfg($live));
+
+ # If we have a theme, duplicate the theme configuration file and modify the
+ # title string to indicate we are doing a UEFI boot. This is useful when
+ # dealing with user bug reports...
+ if (-e $base_theme_txt) {
+ my $uefi_theme_txt = $grub2_dir . "/themes/$theme_name/theme-uefi.txt";
+ cp_f($base_theme_txt, $uefi_theme_txt);
+ run_('sed', '-i', qq(s/title-text:.*/title-text: "$title (UEFI)"/), $uefi_theme_txt);
+ }
+
+ # Create another build directory for temporarily storing the ESP image.
+ my $images_dir = $live->get_builddir . $live->{prefix}{build}{images};
+ mkdir_p($images_dir);
+
+ # Construct an ESP image. This is needed for USB boot.
+ my $esp_image = $images_dir . '/esp.img';
+ eval { rm_rf($esp_image) };
+ run_("/sbin/mkdosfs", "-F12", "-C", $esp_image, "4096");
+ run_("mcopy", "-s", "-i", $esp_image, $efi_root_dir, "::");
+}
+
+sub build_grub2_eltorito_img {
+ my ($live, $output) = @_;
+
+ my @modules = qw(biosdisk iso9660 fat part_msdos all_video font png gfxterm gfxmenu linux
+ keylayouts at_keyboard usb_keyboard configfile echo gettext ls search test);
+
+ run_('grub2-mkimage',
+ '--output', $output,
+ '--prefix', $live->get_media_prefix('boot') . '/grub2',
+ '--format', 'i386-pc-eltorito',
+ @modules
+ );
+}
+
+sub build_grub2_bootx64_efi {
+ my ($live, $output) = @_;
+
+ my @modules = qw(iso9660 fat part_msdos all_video font png gfxterm gfxmenu linux
+ keylayouts at_keyboard usb_keyboard configfile echo gettext ls search test);
+
+ run_('grub2-mkimage',
+ '--output', $output,
+ '--prefix', $live->get_media_prefix('EFI') . '/BOOT',
+ '--format', 'x86_64-efi',
+ @modules
+ );
+}
+
+sub build_grub2_cfg {
+ my ($live, $theme_name, $theme_fonts, $add_lang_menu, $add_kbd_menu) = @_;
+
+ my @loadfonts;
+ if (defined $theme_name) {
+ @loadfonts = map { " loadfont \$prefix/themes/$theme_name/$_" } @$theme_fonts;
+ }
+
+ my $gettext = $add_lang_menu ? '$' : '';
+
+ my $boot_dir = $live->get_media_prefix('boot');
+
+ join("\n",
+ "if [ -z \$initialised ] ; then",
+ " search --no-floppy --set=root -l '" . $live->{media}->get_media_label . "'",
+ " set prefix=(\$root)" . $live->get_media_prefix('boot') . "/grub2",
+ "",
+ " if loadfont \$prefix/fonts/unicode.pf2 ; then",
+ " set gfxmode=1024x768,800x600,auto",
+ " set gfxpayload=keep",
+ " terminal_output gfxterm",
+ " fi",
+ if_($theme_name,
+ "",
+ " if [ x\$uefi == 'xtrue' ] ; then",
+ " set theme=\$prefix/themes/$theme_name/theme-uefi.txt",
+ " else",
+ " set theme=\$prefix/themes/$theme_name/theme.txt",
+ " fi",
+ " export theme",
+ @loadfonts,
+ ),
+ " set initialised=true",
+ " export initialised",
+ "fi",
+ "",
+ "set default=" . get_bootloader_default($live),
+ "set timeout=" . get_bootloader_timeout($live),
+ "",
+ if_($add_lang_menu,
+ "export lang",
+ "export lkbd",
+ "",
+ ),
+ if_($add_kbd_menu,
+ "export kbd",
+ "",
+ ),
+ (map {
+ my ($name, $cmdline) = @$_;
+ join("\n",
+ "menuentry $gettext\"$name\" {",
+ " linux $boot_dir/vmlinuz " . get_default_append($live) . if_($cmdline, " $cmdline"),
+ " initrd $boot_dir/initrd.gz",
+ "}"
+ );
+ } group_by2(@{$live->{media}{bootloader_entries}})),
+ if_($add_lang_menu || $add_kbd_menu,
+ # this acts as a spacer
+ "menuentry '________________________' {",
+ " set dummy=true",
+ "}",
+ ),
+ if_($add_lang_menu,
+ "submenu \"F2: \"$gettext\"Language [\$lang]\" --id language --hotkey f2 {",
+ " source \$prefix/lang-menu.cfg",
+ "}",
+ ),
+ if_($add_kbd_menu,
+ "submenu \"F3: \"$gettext\"Keyboard [\$kbd]\" --id keyboard --hotkey f3 {",
+ " source \$prefix/kbd-menu.cfg",
+ "}",
+ ),
+ "",
+ );
+}
+
+sub get_bootloader_default {
+ my ($live) = @_;
+ defined $live->{media}{bootloader_default} ? $live->{media}{bootloader_default} : 0;
+}
+
+sub get_bootloader_timeout {
+ my ($live) = @_;
+ defined $live->{media}{bootloader_timeout} ? $live->{media}{bootloader_timeout} : 4;
+}
+
+sub get_default_append {
+ my ($live) = @_;
+ my $append = $live->{system}{append};
+ join(" ",
+ "root=mgalive:LABEL=" . $live->{media}->get_media_label,
+ "lang=\$lang kbd=\$kbd",
+ if_($append, $append),
+ if_($live->{system}{vga_mode} && $append !~ /\bvga=\b/,
+ "vga=" . $live->{system}{vga_mode}),
+ );
+}
+
+sub build_lang_menu_cfg {
+ my ($langs, %kbds) = @_;
+ join("\n",
+ "function set_language {",
+ " set lang=\$1",
+ " set lkbd=\$2",
+ " configfile \$prefix/grub.cfg",
+ "}",
+ "",
+ "set default=\$lang",
+ "set timeout=-1",
+ "",
+ "menuentry \$\"[more options after boot]\" { set_language '' '' }",
+ (map {
+ my ($id, $name) = @$_;
+ my $kbd = $kbds{$id};
+ "menuentry '$name' --id $id { set_language $id $kbd }";
+ } (@$langs)),
+ "",
+ );
+}
+
+sub build_kbd_menu_cfg {
+ my ($kbds) = @_;
+ join("\n",
+ "function set_keyboard {",
+ " if [ -z \$kbd ] ; then",
+ " terminal_input at_keyboard",
+ " fi",
+ " set kbd=\$1",
+ " set lkbd=",
+ " keymap \$prefix/layouts/\$kbd.gkb",
+ " configfile \$prefix/grub.cfg",
+ "}",
+ "",
+ "if [ -z \$kbd ] ; then",
+ " set default=\$lkbd",
+ "else",
+ " set default=\$kbd",
+ "fi",
+ "",
+ "set timeout=-1",
+ "",
+ (map {
+ my ($id, $name) = @$_;
+ $name =~ s/"/\\"/g;
+ "menuentry \$\"$name\" --id $id { set_keyboard $id }";
+ } (@$kbds)),
+ "",
+ );
+}
+
+sub build_uefi_grub2_cfg {
+ my ($live) = @_;
+ join("\n",
+ "search --no-floppy --set=root -l '" . $live->{media}->get_media_label . "'",
+ "set prefix=(\$root)" . $live->get_media_prefix('boot') . "/grub2",
+ "",
+ "set uefi=true",
+ "export uefi",
+ "",
+ "configfile \$prefix/grub.cfg",
+ "",
+ );
+}
+
+1;
diff --git a/lib/MGA/DrakISO/BuildISO.pm b/lib/MGA/DrakISO/BuildISO.pm
new file mode 100755
index 0000000..9b7e503
--- /dev/null
+++ b/lib/MGA/DrakISO/BuildISO.pm
@@ -0,0 +1,163 @@
+# Copyright (C) 2005 Mandriva
+# Olivier Blin <oblin@mandriva.com>
+# Copyright (C) 2017 Mageia
+# Martin Whitaker <mageia@martin-whitaker.me.uk>
+#
+# 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.
+
+package MGA::DrakISO::BuildISO;
+
+use strict;
+
+use MDK::Common;
+use common;
+
+use MGA::DrakISO::Live;
+use MGA::DrakISO::Loopback;
+use MGA::DrakISO::Utils;
+
+use MGA::DrakISO::BuildLoop;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(build_live_iso);
+
+###############################################################################
+# Live System
+###############################################################################
+
+sub build_live_iso {
+ my ($live) = @_;
+
+ my $label = $live->{media}->get_media_label or die "the source device must be described by a label\n";
+
+ my $mbr_image = $live->get_absolute_path($live->{media}{mbr_boot_img}) // '/usr/lib/grub/i386-pc/boot_hybrid.img';
+ -e $mbr_image or die "cannot find MBR boot image $mbr_image\n";
+
+ my $esp_image = $live->get_builddir . $live->{prefix}{build}{images} . '/esp.img';
+ -e $esp_image or die "cannot find ESP image $esp_image\n";
+
+ my $dest = $live->get_builddir . $live->{prefix}{build}{dist} . '/' . $live->get_name . '.iso';
+ mkdir_p(dirname($dest));
+
+ build_iso_image(
+ $live,
+ $dest,
+ $label,
+ $mbr_image,
+ $esp_image,
+ $live->get_media_prefix('boot') . '=' . $live->get_builddir . $live->{prefix}{build}{boot},
+ if_($live->{settings}{arch} eq 'x86_64',
+ $live->get_media_prefix('EFI') . '=' . $live->get_builddir . $live->{prefix}{build}{EFI},
+ ),
+ (
+ map {
+ $live->get_media_prefix('loopbacks') . $_ .
+ '=' .
+ $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 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);
+}
+
+###############################################################################
+# Generic
+###############################################################################
+
+# This builds a hybrid ISO capable of both legacy and UEFI boot. The
+# ISO contains a primary iso9660 partition (which is the only thing
+# visible when booting from DVD) and a secondary ESP partition (which
+# is only used for USB UEFI boot).
+#
+# For legacy boot, the system boots the grub2 El Torito image. For DVD
+# boot, this is located via the El Torito catalogue. For USB boot, it
+# is located via the grub2 MBR hybrid boot image, which is stored in
+# the MBR. In both cases the initial grub2 root location is the iso9660
+# partition, so the initial grub2 configuration file will be read from
+# that partition.
+#
+# For UEFI boot, the system boots the grub2 EFI image. For DVD boot,
+# this is located via the El Torito catalogue, and we store it in the
+# iso9660 partition. For USB boot, it must be located in the /EFI/BOOT
+# directory in the ESP. For DVD boot the initial grub2 root location is
+# the iso9660 partition, for USB boot it is the ESP partition, and the
+# initial grub2 configuration file will be located accordingly.
+#
+sub build_iso_image {
+ my ($live, $dest, $label, $mbr_image, $esp_image, @opts) = @_;
+
+ my $boot = $live->get_media_prefix('boot'); # normally '/boot'
+ my $EFI = $live->get_media_prefix('EFI'); # normally '/EFI'
+
+ run_('xorrisofs',
+ '-pad', '-l', '-R', '-J',
+ '-V', $label,
+ '-graft-points',
+ '-hide-rr-moved',
+ '--sort-weight', 0, '/',
+ '--sort-weight', 1, $boot,
+ # for hybrid MBR boot
+ '--grub2-mbr', $mbr_image,
+ '-b', "$boot/grub2/eltorito.img",
+ '-no-emul-boot',
+ '-boot-load-size', 4,
+ '-boot-info-table',
+ '--grub2-boot-info',
+ if_($live->{settings}{arch} eq 'x86_64',
+ # for DVD UEFI boot
+ '-eltorito-alt-boot',
+ '-e', "$EFI/BOOT/bootx64.efi",
+ '-no-emul-boot',
+ # for USB UEFI boot
+ '-part_like_isohybrid',
+ '-iso_mbr_part_type', '0x00',
+ '-append_partition', 2, '0xef', $esp_image,
+ ),
+ if_($dest, '-o', $dest),
+ @opts,
+ ) or die "unable to run xorrisofs\n";
+
+ if ($dest) {
+ my $dir = dirname($dest);
+ my $filename = basename($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";
+ }
+ }
+ }
+}
+
+1;
diff --git a/lib/MGA/DrakISO/BuildLoop.pm b/lib/MGA/DrakISO/BuildLoop.pm
new file mode 100755
index 0000000..5e840d4
--- /dev/null
+++ b/lib/MGA/DrakISO/BuildLoop.pm
@@ -0,0 +1,116 @@
+# Copyright (C) 2005 Mandriva
+# Olivier Blin <oblin@mandriva.com>
+# Copyright (C) 2017 Mageia
+# Martin Whitaker <mageia@martin-whitaker.me.uk>
+#
+# 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.
+
+package MGA::DrakISO::BuildLoop;
+
+use strict;
+
+use MDK::Common;
+use common;
+
+use File::Temp qw(tmpnam);
+
+use MGA::DrakISO::Live;
+use MGA::DrakISO::Loopback;
+use MGA::DrakISO::Utils;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(build_live_loopback_files list_loopback_modules);
+
+sub build_live_loopback_files {
+ my ($live) = @_;
+ # make sure no external filesystems are mounted before creating the loopback
+ umount_external_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 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}};
+}
+
+1;
diff --git a/lib/MGA/DrakISO/BuildRoot.pm b/lib/MGA/DrakISO/BuildRoot.pm
new file mode 100755
index 0000000..7112cd9
--- /dev/null
+++ b/lib/MGA/DrakISO/BuildRoot.pm
@@ -0,0 +1,259 @@
+# Copyright (C) 2005 Mandriva
+# Olivier Blin <oblin@mandriva.com>
+# Copyright (C) 2017 Mageia
+# Martin Whitaker <mageia@martin-whitaker.me.uk>
+#
+# 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.
+
+package MGA::DrakISO::BuildRoot;
+
+use strict;
+
+use MDK::Common;
+use common;
+
+use MGA::DrakISO::Live;
+use MGA::DrakISO::Loopback;
+use MGA::DrakISO::Utils;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(install_live_system customise_live_system);
+
+sub install_live_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(':', $live->get_langs);
+ 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";
+}
+
+sub customise_live_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_external_fs($live);
+
+ umask $previous_umask;
+}
+
+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 = MGA::DrakISO::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 remove_files_from {
+ my ($files, $root) = @_;
+ run_('find', $root, '(', join_lists('-o', map { [ '-name', $_ ] } @$files), ')', '-exec', 'rm', '-r', '{}', ';')
+ if $files && @$files;
+}
+
+sub join_lists {
+ my ($separator, $head, @lists) = @_;
+ @{$head || []}, map { $separator, @$_ } @lists;
+}
+
+sub clean_system_conf_file {
+ my ($live, $file) = @_;
+ substInFile { undef $_ if /^[^#]/ } $live->get_system_root . $file;
+}
+
+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 = $live->get_langs;
+ 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));
+}
+
+1;
diff --git a/lib/MGA/DrakISO/Live.pm b/lib/MGA/DrakISO/Live.pm
index e92fc2c..6fa32af 100644
--- a/lib/MGA/DrakISO/Live.pm
+++ b/lib/MGA/DrakISO/Live.pm
@@ -26,6 +26,14 @@ sub get_set_suffix {
$live->{settings}{set} ? "-$live->{settings}{set}" : "";
}
+sub get_langs {
+ my ($live) = @_;
+ uniq(
+ (ref $live->{regions} ? @{$live->{regions}{$live->{settings}{region}}} : ()),
+ @{$live->{system}{langs_always}}
+ );
+}
+
sub get_builddir {
my ($live) = @_;
$live->{settings}{builddir} . '/' . $live->get_name . $live->get_set_suffix;
@@ -67,4 +75,13 @@ sub get_lib_prefix {
$lib_prefix;
}
+sub get_absolute_path {
+ my ($live, $path) = @_;
+ if (defined $path && substr($path, 0, 1) ne '/') {
+ $live->{settings}{config_root} . '/' . $path;
+ } else {
+ $path;
+ }
+}
+
1;
diff --git a/lib/MGA/DrakISO/Utils.pm b/lib/MGA/DrakISO/Utils.pm
index 1f14241..a2abd17 100644
--- a/lib/MGA/DrakISO/Utils.pm
+++ b/lib/MGA/DrakISO/Utils.pm
@@ -2,13 +2,14 @@ package MGA::DrakISO::Utils;
use MDK::Common;
use common;
+use fs;
use run_program;
use IPC::Open3;
use IO::Select;
use Exporter;
our @ISA = qw(Exporter);
-our @EXPORT = qw(directory_usage run_ run_foreach);
+our @EXPORT = qw(directory_usage run_ run_foreach mount_system_fs umount_external_fs);
sub directory_usage {
my ($dir, $o_apparent) = @_;
@@ -82,4 +83,21 @@ sub device_mkfs {
}
}
+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');
+}
+
+sub umount_external_fs {
+ my ($live) = @_;
+ my $system_root = Cwd::abs_path($live->get_system_root);
+ my @mounts = grep { $_ =~ $system_root } split("\n", cat_('/proc/mounts'));
+ foreach (reverse(@mounts)) {
+ my @field = split(' ' , $_);
+ fs::mount::umount($field[1]);
+ }
+}
+
1;