summaryrefslogtreecommitdiffstats
path: root/lib/MGA/DrakISO/BuildBoot.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MGA/DrakISO/BuildBoot.pm')
-rwxr-xr-xlib/MGA/DrakISO/BuildBoot.pm423
1 files changed, 423 insertions, 0 deletions
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;