summaryrefslogtreecommitdiffstats
path: root/lib/MGA/DrakISO/BuildRoot.pm
diff options
context:
space:
mode:
authorMartin Whitaker <mageia@martin-whitaker.me.uk>2018-01-09 23:17:35 +0000
committerMartin Whitaker <mageia@martin-whitaker.me.uk>2018-01-09 23:17:35 +0000
commit63555aa270059488b013e6c802cca64a8d6eebdc (patch)
tree75c481453e459dc2aea279a86eb89ceedfd0ecf1 /lib/MGA/DrakISO/BuildRoot.pm
parent71146812d733fdc46abe1d77b7d68f9859cdc124 (diff)
downloaddrakiso-63555aa270059488b013e6c802cca64a8d6eebdc.tar
drakiso-63555aa270059488b013e6c802cca64a8d6eebdc.tar.gz
drakiso-63555aa270059488b013e6c802cca64a8d6eebdc.tar.bz2
drakiso-63555aa270059488b013e6c802cca64a8d6eebdc.tar.xz
drakiso-63555aa270059488b013e6c802cca64a8d6eebdc.zip
draklive: allow installer GUI to be used and run as normal user.
Read all configuration from the main config file and automatically generate the auto_inst.cfg.pl file. Run the installer GUI in a nested X server if any items are not specified in the config file. Use sudo to run any steps that need root privileges, to avoid running the X server as root.
Diffstat (limited to 'lib/MGA/DrakISO/BuildRoot.pm')
-rw-r--r--lib/MGA/DrakISO/BuildRoot.pm683
1 files changed, 510 insertions, 173 deletions
diff --git a/lib/MGA/DrakISO/BuildRoot.pm b/lib/MGA/DrakISO/BuildRoot.pm
index 616e3a6..af06769 100644
--- a/lib/MGA/DrakISO/BuildRoot.pm
+++ b/lib/MGA/DrakISO/BuildRoot.pm
@@ -27,8 +27,11 @@ package MGA::DrakISO::BuildRoot;
use strict;
use MDK::Common;
+use Try::Tiny;
use common;
+use File::Temp qw(tmpnam);
+
use MGA::DrakISO::LiveBuild;
use MGA::DrakISO::Loopback;
use MGA::DrakISO::Utils;
@@ -37,163 +40,479 @@ use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(install_live_system customise_live_system);
+###############################################################################
+# System Installation
+###############################################################################
+
# This is the top-level function called to create the basic root filesystem. It
# uses stage2 of the Mageia installer to do the actual work. It is independent
# of any other preparatory step.
#
+# This function is largely derived from drakx_in_chroot.
+#
sub install_live_system {
my ($build) = @_;
- my $repository = $build->{settings}{repository} . '/' . $build->{settings}{arch};
+ print "Installing Live system\n" if $::verbose;
- 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 = $build->get_builddir('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;
- }
+ my $arch = $build->{settings}{arch};
- local %ENV = (
- %ENV,
- (map { "DRAKLIVE_" . uc($_->[0]) => $_->[1] } group_by2(%{$build->{settings}})),
- %{$build->{system}{install_env}},
- );
- $ENV{DRAKLIVE_LANGS} = join(':', $build->get_langs);
- run_({ targetarch => $build->{settings}{arch} },
- 'perl', $drakx_in_chroot,
- $repository,
- $build->get_system_root,
- if_($build->{system}{auto_install}, '--auto_install', $build->{settings}{config_root} . '/' . $build->{system}{auto_install}),
- if_($build->{system}{patch_install}, '--defcfg', $build->{settings}{config_root} . '/' . $build->{system}{patch_install}),
- if_($build->{system}{rpmsrate}, '--rpmsrate', $build->{settings}{config_root} . '/' . $build->{system}{rpmsrate}),
- ($build->{system}{stage2_updates} ? (map { ('--stage2-update', $build->{settings}{config_root} . '/' . $_->[0], $_->[1]) } @{$build->{system}{stage2_updates}}) : ()),
- ) or die "unable to install system chroot\n";
-}
+ my $base_repository = $build->{settings}{repository};
+ my $arch_repository = $base_repository . '/' . $arch;
+ my $remote_method = $arch_repository =~ m!^(ftp|http)://! && $1;
-# This is the top-level function called to customise the root filesystem. It
-# allows a standard Mageia installation to be fine-tuned for use as a Live
-# system. The basic root filesystem must have been prepared before calling
-# this function.
-#
-sub customise_live_system {
- my ($build) = @_;
+ my $chroot = $build->get_chroot_dir;
- my $previous_umask = umask;
- #- workaround buggy installation of directories that are not owned by any packages
- umask 022;
+ my $rooted_stage2 = '/tmp/stage2';
+ my $chroot_stage2 = $chroot . $rooted_stage2;
+
+ my $live_root = $build->get_system_root;
+ if (-e $live_root) {
+ # We want a clean start...
+ umount_all_in_root($live_root);
+ run_as_root('rm', '-rf', $live_root);
+ }
+ mkdir_p($live_root);
+
+ my $Xserver_pid;
+ my $error_message;
+ try {
+ # Mount the directory where we want to install the Live system.
+ mount($chroot . '/mnt', $live_root, '-o bind');
+
+ # Mount the standard system pseudo-filesystems, so that the installer
+ # has a proper environment to run in.
+ mount_system_fs($chroot);
+ mount($chroot . '/sys/kernel/debug', 'none', '-t debugfs');
+
+ # Mount the stage2 installer filesystem.
+ if ($remote_method) {
+ my $local_mdkinst = $chroot . '/tmp/mdkinst.sqfs';
+ system("curl --silent -o $local_mdkinst $arch_repository/install/stage2/mdkinst.sqfs")
+ or die "ERROR: failed to download mdkinst.sqfs from remote repository\n";
+ mount($chroot_stage2, $local_mdkinst, '-t squashfs -o loop,ro');
+
+ } elsif (-d $arch_repository . '/install/stage2/live') {
+ mount($chroot_stage2, $arch_repository . '/install/stage2/live', '-o bind,ro');
+
+ } elsif (-f $arch_repository . '/install/stage2/mdkinst.sqfs') {
+ mount($chroot_stage2, $arch_repository . '/install/stage2/mdkinst.sqfs', '-t squashfs -o loop,ro');
+
+ } else {
+ die "ERROR: failed to find installer stage2\n";
+ }
+
+ # The stage2 installer expects to find the full repository in this
+ # location...
+ mount($chroot . '/tmp/media', $base_repository, '-o bind,ro') if !$remote_method;
+ # and the arch-specific repository in this location.
+ symlinkf('media/' . $arch, $chroot . '/tmp/image');
+ # Because the installer uses the above symlink, relative paths in
+ # the urpmi configuration don't work unless we add this extra link.
+ symlinkf('media/i586', $chroot . '/tmp/i586') if $arch eq 'x86_64';
+
+ # If the user has provided an auto-install configuration file, use it,
+ # otherwise construct one.
+ my $rooted_auto_inst = '/tmp/auto_inst.cfg.pl';
+ my $system_auto_inst = $build->get_absolute_path($build->{system}{auto_install});
+ my $interactive;
+ if (defined $system_auto_inst) {
+ -f $system_auto_inst or die "ERROR: can't find the auto-install configuration file $system_auto_inst\n";
+ cp_f($system_auto_inst, $chroot . $rooted_auto_inst);
+ } else {
+ write_auto_inst_cfg($build, $chroot . $rooted_auto_inst, \$interactive);
+ }
+
+ # Add a few more things to complete the installer's working environment.
+
+ my $etc = $chroot . '/etc';
+ mkdir_p($etc);
+ output($etc . '/hosts', "127.0.0.1 localhost\n");
+
+ my $var = $chroot . '/var';
+ mkdir_p($var);
+
+ create_initial_symlinks($chroot, $rooted_stage2);
+
+ chomp(my $kernel_version = `uname -r`);
+ my $modules_dir = '/modules/' . $kernel_version;
+ output_p($chroot . $modules_dir . $_, "\n")
+ foreach "/lib/$modules_dir/modules.dep", "/lib/$modules_dir/modules.alias";
+
+ # If some of the installer steps will be interactive, we need to start a
+ # nested X server to display the GUI.
+ my $DISPLAY = '';
+ if ($interactive) {
+ my $Xserver = whereis_binary('Xephyr') || whereis_binary('Xnest')
+ or die "ERROR: Xephyr or Xnest not found - cannot run installer GUI\n";
+ my $screen_size = $Xserver =~ /Xephyr/ ? '-screen' : '-geometry';
+ $DISPLAY = ':8';
+ if ($Xserver_pid = fork()) {
+ } else {
+ close(STDOUT); open(STDOUT, '>', '/dev/null');
+ close(STDERR); open(STDERR, '>', '/dev/null');
+ exec($Xserver, $DISPLAY, '-ac', $screen_size, '1024x768');
+ }
+ }
+
+ # Run the installer. The chroot command sets up a new environment,
+ # so we need to set the variables we want after we've entered the
+ # chroot.
+ my $env = join(' ',
+ "PATH=/usr/sbin:/mnt/usr/sbin:/usr/bin:/mnt/usr/bin",
+ "LD_LIBRARY_PATH=/usr/lib:/mnt/usr/lib:/usr/lib64:/mnt/usr/lib64",
+ if_($remote_method, "URLPREFIX=$arch_repository"),
+ "DISPLAY=$DISPLAY",
+ "TERM=linux",
+ "HOME=/",
+ );
+ my $cmd = "/usr/bin/runinstall2 --local_install --auto_install $rooted_auto_inst";
+ $cmd .= "--method $remote_method" if $remote_method;
+ run_in_root($chroot, $arch, 'sh', '-c', "$env $cmd")
+ or die "ERROR: failed to install base system\n";
+ } catch {
+ $error_message = $_;
+ } finally {
+ # During package installation, a dbus daemon may get automatically
+ # launched in the Live system root. We need to kill it before we
+ # can unmount the root filesystem.
+ run_as_root('fuser', '-s', '-k', "$chroot/mnt");
+ # Allow a bit of time for the processes to die.
+ sleep(1);
+ # Now we can unmount everything. The order is important.
+ umount_all_in_root($live_root);
+ umount_all_in_root($chroot);
+ # And finally kill off our nested X server.
+ kill(15, $Xserver_pid) if $Xserver_pid;
+ };
+ defined $error_message && die $error_message;
+}
- mount_system_fs($build);
+sub write_auto_inst_cfg {
+ my ($build, $file, $interactive) = @_;
+
+ my $region = $build->{settings}{region};
+ my $enabled_media = $build->{system}{enabled_media};
+ my $rpmsrate_flags = $build->{system}{rpmsrate_flags};
+ my $rpmsrate_level = $build->{system}{rpmsrate_level} || 5;
+ my $include_packages = $build->{system}{include_packages};
+ my $exclude_packages = $build->{system}{exclude_packages};
+ my $preferred_packages = $build->{system}{preferred_packages};
+ my @desktops = split(/\|/, $build->{settings}{desktop});
+ my $default_user = $build->{settings}{default_user};
+ my $post_install_nr = $build->{system}{post_install_nr};
+ my $post_install = $build->{system}{post_install};
+
+ $$interactive = !$region || !$enabled_media || !$rpmsrate_flags || !$default_user;
+
+ my @text;
+ push @text, (
+ "\$o = {",
+ " security => 1,",
+ " authentication => {",
+ " shadow => 1,",
+ " local => 1,",
+ " blowfish => 1,",
+ " },",
+ );
+ push @text, (
+ " interactiveSteps => [",
+ if_(!$region,
+ " 'selectLanguage',",
+ " 'selectKeyboard',",
+ ),
+ if_(!$enabled_media,
+ " 'chooseMedia',",
+ ),
+ if_(!$rpmsrate_flags,
+ " 'choosePackages',",
+ ),
+ if_(!$default_user,
+ " 'addUser',",
+ ),
+ " ],",
+ ) if $$interactive;
+ push @text, (
+ " media => [",
+ " {",
+ " type => 'media_cfg',",
+ " url => 'drakx://media',",
+ " selected_names => '" . join(', ', @$enabled_media) . "',",
+ " },",
+ " ],",
+ " # temporary (?) fix for mga#12299",
+ " enabled_media => [ " . join(', ', map { "'$_'" } @$enabled_media) . " ],",
+ ) if $enabled_media;
+ push @text, (
+ " rpmsrate_flags_chosen => {",
+ (map { " $_ => 1," } @$rpmsrate_flags),
+ " },",
+ " compssListLevel => $rpmsrate_level,",
+ ) if $rpmsrate_flags;
+ push @text, (
+ " default_packages => [",
+ (map { " '$_'," } @$include_packages),
+ " ],",
+ ) if $include_packages;
+ push @text, (
+ " skipped_packages => [",
+ (map { " '$_'," } @$exclude_packages),
+ " ],",
+ ) if $exclude_packages;
+ push @text, (
+ " preferred_packages => '" . join(', ', @$preferred_packages) . "',",
+ ) if $preferred_packages;
+ push @text, (
+ " meta_class => 'desktop',",
+ " desktop => '$desktops[0]',",
+ ) if @desktops;
+ push @text, (
+ " autologin => '$default_user',",
+ " users => [",
+ " {",
+ " realname => '',",
+ " name => '$default_user',",
+ " shell => '/bin/bash',",
+ " icon => 'default',",
+ " groups => [],",
+ " gid => '',",
+ " uid => '',",
+ " },",
+ " ],",
+ " superuser => {",
+ " pw => '',",
+ " realname => 'root',",
+ " shell => '/bin/bash',",
+ " home => '/root',",
+ " gid => '0',",
+ " uid => '0',",
+ " },",
+ ) if $default_user;
+ push @text, (
+ " locale => {",
+ " lang => 'en_US',",
+ " country => 'US',",
+ " IM => undef,",
+ if_($region ne 'all',
+ " langs => { " . join(', ', map { "$_ => 1" } $build->get_langs) . " },",
+ ),
+ if_($region eq 'all',
+ " langs => { all => 1 },",
+ ),
+ " utf8 => 1,",
+ " },",
+ " keyboard => {",
+ " KEYBOARD => 'us',",
+ " KEYTABLE => 'us',",
+ " KBCHARSET => 'C',",
+ " GRP_TOGGLE => '',",
+ " },",
+ " timezone => {",
+ " ntp => undef,",
+ " timezone => 'America/New_York',",
+ " UTC => 1,",
+ " },",
+ ) if $region;
+ push @text, (
+ " postInstallNonRooted => \"$post_install_nr\","
+ ) if $post_install_nr;
+ push @text, (
+ " postInstall => \"$post_install\","
+ ) if $post_install;
+ push @text, (
+ " X => { disabled => 1 },",
+ " keep_unrequested_dependencies => 0,",
+ " match_all_hardware => 1,",
+ " autoExitInstall => 1,",
+ "};",
+ );
+ output($file, map { "$_\n" } @text);
+}
- #- copy resolv.conf for name resolution to work when adding media
- cp_f("/etc/resolv.conf", $build->get_system_root . "/etc/");
+sub create_initial_symlinks {
+ my ($chroot, $rooted_stage2) = @_;
- #- remove previous draklive leftovers if needed
- run_({ root => $build->get_system_root }, 'urpmi.removemedia', '-a');
+ my $chroot_stage2 = $chroot . $rooted_stage2;
- foreach (@{$build->{system}{additional_media}}) {
- run_({ root => $build->get_system_root }, 'urpmi.addmedia', if_($_->{distrib}, '--distrib'), $_->{name}, $_->{path})
- or die "unable to add media from $_->{path}\n";
- @{$_->{packages} || []} or next;
- run_({ root => $build->get_system_root, targetarch => $build->{settings}{arch} },
- 'urpmi', '--auto', '--no-verify-rpm', if_(!$_->{distrib}, '--searchmedia', $_->{name}), @{$_->{packages}})
- or die "unable to install packages from $_->{path}\n";
+ foreach my $line (cat_or_die($chroot_stage2 . '/usr/share/symlinks')) {
+ my ($from, $to_) = split(' ', $line);
+ my $to = $chroot . ($to_ || $from);
+ $from = $rooted_stage2 . $from if !$to_;
+ symlinkf($from, $to) or die "ERROR: symlinking $to failed\n";
}
- #- additional rpms may have dependencies in additional media
- if (@{$build->{system}{rpms} || []}) {
- my $rpm_tmp_dir = '/tmp/draklive_rpms';
- mkdir_p($build->get_system_root . $rpm_tmp_dir);
- cp_f((map { $build->{settings}{config_root} . '/' . $_ } @{$build->{system}{rpms}}), $build->get_system_root . $rpm_tmp_dir);
- run_({ root => $build->get_system_root, targetarch => $build->{settings}{arch} },
- 'urpmi', '--auto', '--no-verify-rpm',
- map { $rpm_tmp_dir . '/' . basename($_) } @{$build->{system}{rpms}})
- or die "unable to install additional system rpms\n";
- rm_rf($build->get_system_root . $rpm_tmp_dir);
- }
+ my $from = $rooted_stage2 . '/usr';
+ my $to = $chroot . '/usr';
+ symlinkf $from, $to or die "ERROR: symlinking $to failed\n";
- #- remove urpmi media added by drakx-in-chroot and additional media, they're unusable
- run_({ root => $build->get_system_root }, 'urpmi.removemedia', '-a');
-
- my $erase = join(' ', @{$build->{system}{erase_rpms} || []});
- run_({ root => $build->get_system_root, targetarch => $build->{settings}{arch} },
- 'sh', '-c', "rpm -qa $erase | xargs rpm -e ") if $erase;
-
- run_({ root => $build->get_system_root }, 'systemctl', if_($::verbose < 2, '-q'), 'disable', $_ . '.service')
- foreach @{$build->{system}{disable_services}};
- run_({ root => $build->get_system_root }, 'systemctl', if_($::verbose < 2, '-q'), 'disable', $_ . '.timer')
- foreach @{$build->{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 => $build->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($build, $_)
- foreach qw(/etc/mtab /etc/iftab /etc/shorewall/interfaces /etc/mdadm.conf),
- if_(!$build->{system}{skip_fstab}, '/etc/fstab');
- unlink($_) foreach map { glob($build->get_system_root . $_) } @{$build->{system}{remove_files} || []};
-
- if ($build->{system}{modules_conf}) {
- local $::prefix = $build->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, $build->{system}{modules_conf});
- $modules_conf->write;
+ foreach my $dir ('/bin', '/sbin', '/lib', '/lib64') {
+ $from = 'usr' . $dir;
+ $to = $chroot . $dir;
+ symlinkf($from, $to) or die "ERROR: symlinking $to failed\n";
}
+}
- my $mount_options = $build->get_media_setting('mount_options') || "defaults";
- output_with_perm($build->get_system_root . '/etc/fstab', 0644,
- $build->{mount}{overlay}
- ? "none / $build->{mount}{overlay} $mount_options 0 0\n"
- : $build->get_media_setting('source') . " / " . $build->get_media_setting('fs') . " $mount_options 1 1\n"
- ) unless $build->{system}{skip_fstab};
+###############################################################################
+# System Customisation
+###############################################################################
- #- interactive mode can lead to race in initscripts
- #- (don't use addVarsInSh from MDK::Common, it breaks shell escapes)
- substInFile { s/^PROMPT=.*/PROMPT=no/ } $build->get_system_root . '/etc/sysconfig/init';
+# This is the top-level function called to customise the root filesystem. It
+# allows the standard Mageia installation to be fine-tuned for use as a Live
+# system. The basic root filesystem must have been prepared before calling
+# this function.
+#
+sub customise_live_system {
+ my ($build) = @_;
- configure_draklive_resize($build);
+ print "Customising Live system\n" if $::verbose;
- if ($build->{system}{preselect_kdm_user}) {
- #- preselect specified user in kdm
- my $kdm_cfg = $build->get_system_root . '/etc/kde/kdm/kdmrc';
- update_gnomekderc($kdm_cfg, 'X-:0-Greeter' => (PreselectUser => 'Default', DefaultUser => $build->{system}{preselect_kdm_user})) if -f $kdm_cfg;
- }
+ my $arch = $build->{settings}{arch};
+ my $root = $build->get_system_root;
- #- apply patches and install files after the configuration is cleaned
- #- to allow special configuration files (especially modprobe.preload)
- foreach (@{$build->{system}{patches}}) {
- my $patch = $build->{settings}{config_root} . '/' . $_;
- my @args = ('-p0', '-d', $build->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";
- }
+ # Workaround buggy installation of directories that are not owned by any
+ # packages.
+ my $previous_umask = umask;
+ umask 022;
- copy_files_to($build, $build->{system}{files}, $build->get_system_root);
- my @no_install_files = map { $_->[1] } grep { $_->[2] && $_->[2]{no_install} } @{$build->{system}{files}};
- output_p($build->get_system_root . '/etc/draklive-install.d/remove.d/draklive', map { "$_\n" } @no_install_files);
+ my $error_message;
+ try {
+ mount_system_fs($root);
+
+ # Copy resolv.conf for name resolution to work when adding media.
+ copy_to_root($root, '/etc/', undef, '/etc/resolv.conf');
- eval { rm_rf($build->get_builddir('files')) };
- mkdir_p($build->get_builddir('files'));
- if ($build->{media}{files}) {
- copy_files_to($build, $build->{media}{files}, $build->get_builddir('files'));
- }
- remove_files_from($build->{media}{remove_files}, $build->get_builddir('files'));
+ # Remove urpmi media added by drakx-in-chroot, they're unusable.
+ run_in_root($root, undef, 'urpmi.removemedia', if_($::verbose < 3, '-q'), '-a');
+
+ print "..adding additional media\n" if $::verbose > 1;
- run_({ targetarch => $build->{settings}{arch} },
- "chroot", $build->get_system_root, "bash", "-c", $build->{system}{final_fixes}) if $build->{system}{final_fixes};
+ # Add additional media for installing RPMs not in the main repository.
+ foreach (@{$build->{system}{additional_media}}) {
+ run_in_root($root, undef, 'urpmi.addmedia', if_($::verbose < 2, '-q'),
+ if_($_->{distrib}, '--distrib'), $_->{name}, $_->{path})
+ or die "ERROR: unable to add media from $_->{path}\n";
- clean_system_conf_file($build, "/etc/resolv.conf");
- write_dist_lists($build);
+ @{$_->{packages} || []} or next;
+
+ run_in_root($root, $arch, 'urpmi', if_($::verbose < 3, '-q'), '--auto', '--no-verify-rpm',
+ if_(!$_->{distrib}, '--searchmedia', $_->{name}), @{$_->{packages}})
+ or die "ERROR: unable to install packages from $_->{path}\n";
+ }
- umount_all_in_chroot($build);
+ print "..installing additional packages\n" if $::verbose > 1;
+
+ # Additional rpms may have dependencies in additional media.
+ if (@{$build->{system}{rpms} || []}) {
+ my $rpm_tmp_dir = '/tmp/draklive_rpms/';
+ mkdir_in_root($root, $rpm_tmp_dir);
- umask $previous_umask;
+ my @rpm_files = map { $build->get_absolute_path($_) } @{$build->{system}{rpms}};
+ copy_to_root($root, $rpm_tmp_dir, undef, @rpm_files);
+
+ @rpm_files = map { $rpm_tmp_dir . basename($_) } @{$build->{system}{rpms}};
+ run_in_root($root, $arch, 'urpmi', if_($::verbose < 3, '-q'), '--auto', '--no-verify-rpm', @rpm_files)
+ or die "ERROR: unable to install additional system rpms\n";
+
+ rm_in_root($root, $rpm_tmp_dir);
+ }
+
+ # Remove any additional media.
+ if (@{$build->{system}{additional_media}}) {
+ run_in_root($root, undef, 'urpmi.removemedia', if_($::verbose < 3, '-q'),'-a');
+ }
+
+ print "..removing unwanted packages\n" if $::verbose > 1;
+
+ # Remove any packages as requested by the user.
+ my @erase = @{$build->{system}{erase_rpms} || []};
+ run_in_root($root, $arch, 'rpm -e', @erase) if @erase;
+
+ print "..disabling unwanted services\n" if $::verbose > 1;
+
+ # Disable services as requested by the user.
+ foreach (@{$build->{system}{disable_services}}) {
+ run_in_root($root, undef, 'systemctl', if_($::verbose < 3, '-q'), 'disable', "$_.service");
+ }
+ foreach (@{$build->{system}{disable_timers}}) {
+ run_in_root($root, undef, 'systemctl', if_($::verbose < 3, '-q'), 'disable', "$_.timer");
+ }
+
+ print "..adjusting system files\n" if $::verbose > 1;
+
+ # Make sure harddrake is run:
+ # if previous HW config file is empty, we assume DrakX has just completed the installation
+ # (do it in chroot, or else Storable from the build system may write an incompatible config file)
+ run_in_root($root, undef, 'perl', '-MStorable', '-e', qq(Storable::store({ UNKNOWN => {} }, '/etc/sysconfig/harddrake2/previous_hw')));
+
+ # Remove some build-machine specific configuration.
+ foreach (qw(/etc/shorewall/interfaces /etc/mdadm.conf)) {
+ clean_system_conf_file($root . $_);
+ }
+
+ # Create fstab.
+ my $mount_options = $build->get_media_setting('mount_options') || 'defaults';
+ my $fstab_entry;
+ if ($build->{mount}{overlay}) {
+ $fstab_entry = "none / $build->{mount}{overlay} $mount_options 0 0";
+ } else {
+ $fstab_entry = $build->get_media_setting('source') . " / " . $build->get_media_setting('fs') . " $mount_options 1 1";
+ }
+ output_to_root($root, '/etc/fstab', 0644, $fstab_entry);
+
+ # Interactive mode can lead to race in initscripts.
+ run_as_root('sed', '-i', 's/^PROMPT=.*/PROMPT=no/', $root . '/etc/sysconfig/init');
+
+ configure_draklive_resize($build);
+
+ print "..copying additional files\n" if $::verbose > 1;
+
+ # Copy extra files as requested by the user.
+ foreach (@{$build->{system}{files}}) {
+ my ($source, $dest, $o_opts) = @$_;
+ mkdir_in_root($root, $dest =~ m|/$| ? $dest : dirname($dest));
+ my @sources = glob__($build->get_absolute_path($source));
+ my $mode = $o_opts && $o_opts->{mode};
+ copy_to_root($root, $dest, $mode, @sources);
+ }
+ my @no_install_files = map { $_->[1] } grep { $_->[2] && $_->[2]{no_install} } @{$build->{system}{files}};
+ my $installer_file = '/etc/draklive-install.d/remove.d/draklive';
+ mkdir_in_root($root, dirname($installer_file));
+ output_to_root($root, $installer_file, undef, @no_install_files);
+
+ print "..removing unwanted files\n" if $::verbose > 1;
+
+ # Remove any files as requested by the user.
+ my @remove = @{$build->{system}{remove_files} || []};
+ rm_in_root($root, @remove) if @remove;
+
+ print "..applying patches\n" if $::verbose > 1;
+
+ # Apply patches as requested by the user.
+ foreach (@{$build->{system}{patches}}) {
+ my $patch_file = $build->get_absolute_path($_);
+ my @patch_cmd = ( 'patch', '-p0', '-d', $root, '-i', $patch_file, if_($::verbose < 3, '-s') );
+ run_as_root(join(' ', @patch_cmd, '--dry-run -f -R > /dev/null')) || run_as_root(@patch_cmd)
+ or die "ERROR: unable to apply patch $patch_file\n";
+ }
+
+ print "..performing final fixes\n" if $::verbose > 1;
+
+ # Perform any final fixes as requested by the user.
+ if ($build->{system}{final_fixes}) {
+ run_in_root($root, $arch, 'bash', '-c', $build->{system}{final_fixes});
+ }
+
+ # Final cleanup.
+ clean_system_conf_file($root . '/etc/resolv.conf');
+
+ write_dist_lists($build);
+ } catch {
+ $error_message = $_;
+ } finally {
+ umount_all_in_root($root);
+ umask $previous_umask;
+ };
+ defined $error_message && die $error_message;
}
sub configure_draklive_resize {
@@ -202,46 +521,23 @@ sub configure_draklive_resize {
my $resizable_loopback = find { $_->{min_size} } @{$build->{mount}{dirs} || []};
if ($resizable_loopback) {
my $ext = $loop_types{$resizable_loopback->{type}}{extension};
- output($build->get_system_root . '/etc/sysconfig/draklive-resize', <<EOF);
-DRAKLIVE_RESIZE=yes
-LOOPBACK=/live/media/loopbacks$resizable_loopback->{path}$ext
-TYPE=$resizable_loopback->{fs}
-MIN_SIZE=$resizable_loopback->{min_size}
-MOUNT=/live$resizable_loopback->{mountpoint}_resized
-OLD_MOUNT=/live$resizable_loopback->{mountpoint}
-UNION=/
-EOF
+ my @text = (
+ "DRAKLIVE_RESIZE=yes",
+ "LOOPBACK=/live/media/loopbacks$resizable_loopback->{path}$ext",
+ "TYPE=$resizable_loopback->{fs}",
+ "MIN_SIZE=$resizable_loopback->{min_size}",
+ "MOUNT=/live$resizable_loopback->{mountpoint}_resized",
+ "OLD_MOUNT=/live$resizable_loopback->{mountpoint}",
+ "UNION=/",
+ );
+ output_to_root($build->get_system_root, '/etc/sysconfig/draklive-resize', undef, @text);
}
}
-sub copy_files_to {
- my ($build, $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__($build->{settings}{config_root} . '/' . $source);
- print "copying @sources to $dest\n" if $::verbose > 1;
- 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 ($build, $file) = @_;
- substInFile { undef $_ if /^[^#]/ } $build->get_system_root . $file;
+ my ($file) = @_;
+
+ run_as_root('sed', '-i', '/^[^#]/d', $file) if -f $file;
}
sub write_dist_lists {
@@ -250,19 +546,19 @@ sub write_dist_lists {
my $lists_dir = $build->get_builddir('dist');
mkdir_p($lists_dir);
- run_("chroot " . $build->get_system_root . " rpm -qa | sort > " .
- $lists_dir . '/' . $build->get_name . '.lst');
+ my $root = $build->get_system_root;
- run_("chroot " . $build->get_system_root . " rpm -qa --qf '%{name}\n' | sort > " .
- $lists_dir . '/' . $build->get_name . '.lst.names');
+ run_in_root($root, undef, "rpm -qa | sort > " .
+ $lists_dir . '/' . $build->get_name . '.lst');
- run_("chroot " . $build->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 . '/' . $build->get_name . '.lst.full');
+ run_in_root($root, undef, "rpm -qa --qf '%{name}\n' | sort > " .
+ $lists_dir . '/' . $build->get_name . '.lst.names');
- run_("chroot " . $build->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 . '/' . $build->get_name . '.lst.leaves');
+ run_in_root($root, undef, 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 . '/' . $build->get_name . '.lst.full');
+
+ run_in_root($root, undef, 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 . '/' . $build->get_name . '.lst.leaves');
require lang;
my @live_langs = $build->get_langs;
@@ -271,4 +567,45 @@ sub write_dist_lists {
output_p($langs_file, map { lang::l2name($_) . " (" . $_ . ")\n" } sort(@langs));
}
+###############################################################################
+# Helper Functions
+###############################################################################
+
+sub mkdir_in_root {
+ my ($root, $dir) = @_;
+
+ run_as_root('mkdir', '-p', $root . $dir)
+ or die "ERROR: failed to make directory $dir in Live system root\n";
+}
+
+sub copy_to_root {
+ my ($root, $dest, $mode, @files) = @_;
+
+ run_as_root('cp', '-af', @files, $root . $dest)
+ or die "ERROR: failed to copy file to $dest in Live system root\n";
+
+ return if !defined $mode;
+
+ run_as_root('chmod', sprintf("%o", $mode), $root . $dest)
+ or die "ERROR: failed to change mode of $dest in Live system root\n";
+}
+
+sub output_to_root {
+ my ($root, $file, $mode, @text) = @_;
+
+ my $temp_file = tmpnam();
+ output($temp_file, map { "$_\n" } @text);
+ chmod($mode, $temp_file) if $mode;
+
+ run_as_root('mv', $temp_file, $root . $file)
+ or die "ERROR: failed to write $file in Live system root\n";
+}
+
+sub rm_in_root {
+ my ($root, @targets) = @_;
+
+ run_as_root('rm', '-rf', map { $root . $_ } @targets)
+ or die "ERROR: failed to remove files in Live system root\n";
+}
+
1;