From 63555aa270059488b013e6c802cca64a8d6eebdc Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Tue, 9 Jan 2018 23:17:35 +0000 Subject: 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. --- lib/MGA/DrakISO/BuildRoot.pm | 683 ++++++++++++++++++++++++++++++++----------- 1 file changed, 510 insertions(+), 173 deletions(-) (limited to 'lib/MGA/DrakISO/BuildRoot.pm') 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', <{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; -- cgit v1.2.1