diff options
-rwxr-xr-x | draklive2 | 965 | ||||
-rwxr-xr-x | lib/MGA/DrakISO/BuildBoot.pm | 423 | ||||
-rwxr-xr-x | lib/MGA/DrakISO/BuildISO.pm | 163 | ||||
-rwxr-xr-x | lib/MGA/DrakISO/BuildLoop.pm | 116 | ||||
-rwxr-xr-x | lib/MGA/DrakISO/BuildRoot.pm | 259 | ||||
-rw-r--r-- | lib/MGA/DrakISO/Live.pm | 17 | ||||
-rw-r--r-- | lib/MGA/DrakISO/Utils.pm | 20 |
7 files changed, 1055 insertions, 908 deletions
@@ -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; |