# Copyright (C) 2005 Mandriva # Olivier Blin # Copyright (C) 2017 Mageia # Martin Whitaker # # 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::LiveBuild; 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 ($build) = @_; my $repository = $build->{settings}{repository} . '/' . $build->{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 = $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; } 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"; } sub customise_live_system { my ($build) = @_; my $previous_umask = umask; #- workaround buggy installation of directories that are not owned by any packages umask 022; mount_system_fs($build); #- copy resolv.conf for name resolution to work when adding media cp_f("/etc/resolv.conf", $build->get_system_root . "/etc/"); #- remove previous draklive leftovers if needed run_({ root => $build->get_system_root }, 'urpmi.removemedia', '-a'); 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"; } #- 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); } #- 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', 'disable', $_ . '.service') foreach @{$build->{system}{disable_services}}; run_({ root => $build->get_system_root }, 'systemctl', '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; } my $mount_options = $build->{media}->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->{media}->get_media_setting('source') . " / " . $build->{media}->get_media_setting('fs') . " $mount_options 1 1\n" ) unless $build->{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/ } $build->get_system_root . '/etc/sysconfig/init'; configure_draklive_resize($build); 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; } #- 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"; } 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); 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')); run_({ targetarch => $build->{settings}{arch} }, "chroot", $build->get_system_root, "bash", "-c", $build->{system}{postInstall}) if $build->{system}{postInstall}; clean_system_conf_file($build, "/etc/resolv.conf"); write_dist_lists($build); umount_external_fs($build); umask $previous_umask; } sub configure_draklive_resize { my ($build) = @_; 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 } } 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 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 ($build, $file) = @_; substInFile { undef $_ if /^[^#]/ } $build->get_system_root . $file; } sub write_dist_lists { my ($build) = @_; 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'); run_("chroot " . $build->get_system_root . " rpm -qa --qf '%{name}\n' | sort > " . $lists_dir . '/' . $build->get_name . '.lst.names'); 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_("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'); require lang; my @live_langs = $build->get_langs; my @langs = grep { member($_, @live_langs) || member(lang::locale_to_main_locale($_), @live_langs) } lang::list_langs(); my $langs_file = $lists_dir . '/' . $build->get_name . '.langs'; output_p($langs_file, map { lang::l2name($_) . " (" . $_ . ")\n" } sort(@langs)); } 1;