diff options
Diffstat (limited to 'perl-install/install/media.pm')
-rw-r--r-- | perl-install/install/media.pm | 1008 |
1 files changed, 1008 insertions, 0 deletions
diff --git a/perl-install/install/media.pm b/perl-install/install/media.pm new file mode 100644 index 000000000..8944cff34 --- /dev/null +++ b/perl-install/install/media.pm @@ -0,0 +1,1008 @@ +package install::media; # $Id$ + +use strict; + +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(getFile_ getAndSaveFile_ getAndSaveFile_media_info packageMedium); + +use common; +use fs::type; + + +#- list of fields for {phys_medium} : +#- device +#- finalpath +#- from_iso +#- fs_type +#- is_suppl (is a supplementary media) +#- isMounted +#- loopback_device +#- loopback_file +#- method +#- mntpoint +#- name (text description, same as first medium {name}) +#- real_mntpoint +#- rel_path (for isofiles and cdrom) +#- url + +#- list of fields for {mediums} : +#- end (last rpm id, undefined iff not selected) +#- fakemedium ("$name ($rpmsdir)", used locally by urpmi) +#- hdlist +#- hdlist_size +#- key_ids (hashref, values are key ids) +#- name (text description) +#- pubkey (array containing all the keys to import) +#- phys_medium +#- rpmsdir +#- selected +#- size (in MB) +#- start (first rpm id, undefined iff not selected) +#- synthesis_hdlist_size +#- update (for install_urpmi) + + +our $postinstall_rpms = ''; +my %mounted_media; + +sub free_medium_id { + my ($mediums) = @_; + int(@$mediums); +} + +sub allMediums { + my ($packages) = @_; + + @{$packages->{mediums}}; +} + +sub phys_media { + my ($packages) = @_; + + uniq(map { $_->{phys_medium} } @{$packages->{mediums}}); +} + +sub pkg2media { + my ($mediums, $p) = @_; + $p or internal_error("invalid package"); + + find { + $_->{selected} && + $p->id >= $_->{start} && $p->id <= $_->{end}; + } @$mediums; +} + +sub packageMedium { + my ($packages, $p) = @_; + + pkg2media($packages->{mediums}, $p) || {}; +} +sub packagesOfMedium { + my ($packages, $medium) = @_; + + @{$packages->{depslist}}[$medium->{start} .. $medium->{end}]; +} + +sub first_medium { + my ($packages) = @_; + $packages->{mediums}[0]; +} + +sub path { + my ($phys_m, $f) = @_; + + ($phys_m->{real_mntpoint} || fs::get::mntpoint_prefixed($phys_m)) . $phys_m->{rel_path} . '/' . $f; +} + +sub rel_rpm_file { + my ($medium, $f) = @_; + if (my ($arch) = $f =~ m|\.([^\.]*)\.rpm$|) { + $f = "$medium->{rpmsdir}/$f"; + $f =~ s/%{ARCH}/$arch/g; + $f =~ s,^/+,,g; + } + $f; +} + +sub umount_phys_medium { + my ($phys_m) = @_; + + my $ok = eval { + fs::mount::umount_part($phys_m); + delete $phys_m->{real_mntpoint}; #- next time, we can mount it in the dest dir + 1; + }; + if ($@) { + log::l("umount phys_medium $phys_m->{url} failed ($@)"); + log::l("files still open: ", readlink($_)) foreach map { glob_("$_/fd/*") } glob_("/proc/*"); + } + $ok; +} +sub mount_phys_medium { + my ($phys_m, $o_rel_file, $b_force_change) = @_; + + if (!$b_force_change) { + eval { fs::mount::part($phys_m) }; + return if $@; + } + my $ok = !$o_rel_file || -e path($phys_m, $o_rel_file); + + if ($phys_m->{method} eq 'cdrom' && ($b_force_change || !$ok)) { + $ok = $::o->ask_change_cd($phys_m, $o_rel_file); + } + $ok; +} + +sub umount_media { + my ($packages) = @_; + + #- we don't bother umounting first phys medium if clp is not on disk + #- (this is mainly for nfs installs using install/stage2/live) + my @l = phys_media($packages); + shift @l if !$install::any::compressed_image_on_disk && $l[0]{is_stage2_phys_medium}; + + umount_phys_medium($_) foreach @l; + umount_phys_medium($_) foreach grep { $_ } map { $_->{loopback_device} } @l; +} + +sub url_respect_privacy { + my ($url) = @_; + + $url =~ s!ftp://.*?\@!ftp://xxx@!; + $url; +} +sub phys_medium_to_string { + my ($phys_m) = @_; + url_respect_privacy($phys_m->{url}) . ($phys_m->{name} ? " ($phys_m->{name})" : ''); +} + +sub stage2_mounted_medium { + my ($method, $rel_path) = @_; + + my ($device, $real_mntpoint, $fs_type, $url); + if ($method eq 'nfs') { + (my $server, my $nfs_path, $real_mntpoint, $fs_type) = cat_("/proc/mounts") =~ m!(\S+):(\S+)\s+(/tmp/media)\s+(\S+)!; + $device = "$server:$nfs_path"; + $url = "nfs://$server$nfs_path$rel_path"; + } elsif ($method eq 'disk') { + ($device, $real_mntpoint, $fs_type) = cat_("/proc/mounts") =~ m!/dev/(\S+)\s+(/tmp/media)\s+(\S+)!; + $url = "disk://$device$rel_path"; + } elsif ($method eq 'cdrom') { + ($device, $real_mntpoint, $fs_type) = cat_("/proc/mounts") =~ m!/dev/(\S+)\s+(/tmp/media)\s+(\S+)!; + $url = "cdrom:/" . ($rel_path || '/'); + } else { + ($device, $real_mntpoint, $fs_type) = cat_("/proc/mounts") =~ m!(?:/dev/)?(\S+)\s+(/tmp/media)\s+(\S+)!; + $url = "file:/" . ($rel_path || '/'); + } + $real_mntpoint or internal_error("no real_mntpoint"); + +{ + method => $method, rel_path => $rel_path, isMounted => 1, + device => $device, url => $url, + real_mntpoint => $real_mntpoint, fs_type => $fs_type, + }; +} + +#- used once at beginning of install +sub stage2_phys_medium { + my ($method) = @_; + + if ($method eq 'ftp' && !$ENV{URLPREFIX}) { + my $user = $ENV{LOGIN} && ($ENV{LOGIN} . ($ENV{PASSWORD} && ":$ENV{PASSWORD}") . '@'); + $ENV{URLPREFIX} = "ftp://$user$ENV{HOST}/$ENV{PREFIX}"; + } + if ($method eq 'http' || $method eq 'ftp') { + { method => $method, url => $ENV{URLPREFIX} }; + } elsif ($method =~ /(.*)-iso$/) { + my $dir_method = $1; + my $rel_path = readlink('/tmp/image') =~ m!loop/*(/.*)! ? $1 : ''; + + my $rel_iso = $ENV{ISOPATH} =~ m!media/*(/.*)! ? $1 : ''; + my ($dir_url, $iso) = (dirname($rel_iso), basename($rel_iso)); + + my $dir_medium = stage2_mounted_medium($dir_method, $dir_url eq '/' ? '' : $dir_url); + my $phys_m = iso_phys_media($dir_medium, $iso, ''); + $phys_m->{real_mntpoint} = '/tmp/loop'; + $phys_m->{real_device} = cat_("/proc/mounts") =~ m!(/dev/\S+)\s+/tmp/loop\s! && $1; + $phys_m->{isMounted} = 1; + $phys_m->{rel_path} = $rel_path; + $phys_m; + } else { + my $rel_path = readlink('/tmp/image') =~ m!media/*(/.*)! ? $1 : ''; + stage2_mounted_medium($method, $rel_path); + } +} + +#- return true if the medium is available +#- ($o_rel_file is only used for removable media) +sub change_phys_medium { + my ($phys_m, $o_rel_file, $o_packages) = @_; + + undef $o_rel_file if $phys_m->{unknown_CD}; #- don't take into account the wanted file + + log::l("change_phys_medium " . phys_medium_to_string($phys_m) . + ($o_rel_file ? " for file $o_rel_file" : '')); + + !$phys_m->{isMounted} && $phys_m->{mntpoint} or return 1; #- nothing to do in such case. + + #- if a cdrom was mounted and we want another one, do not try to mount cdrom just after umounting it + my $force_change = $phys_m->{method} eq 'cdrom' && $mounted_media{cdrom}; + + if (my $current = $mounted_media{$phys_m->{method}}) { + setup_postinstall_rpms($::o, $o_packages, $current) if $o_packages && $phys_m->{method} eq 'cdrom' && $::o->isa('interactive'); + umount_phys_medium($current) or return; + delete $mounted_media{$phys_m->{method}}; + } + mount_phys_medium($phys_m, $o_rel_file, $force_change) or return; + phys_medium_is_mounted($phys_m); + 1; +} + +sub phys_medium_is_mounted { + my ($phys_m) = @_; + if (member($phys_m->{method}, 'cdrom', 'iso')) { + #- we can't have more than one cdrom mounted at once + #- we limit the number of iso files mounted at once + $mounted_media{$phys_m->{method}} = $phys_m; + } +} + +sub associate_phys_media { + my ($all_hds, $main_phys_medium, $hdlists) = @_; + + my ($main_name, @other_names) = uniq(map { $_->{name} } @$hdlists); + + my @other_phys_media = + $main_phys_medium->{method} eq 'iso' ? + get_phys_media_iso($all_hds, $main_phys_medium, \@other_names) : + $main_phys_medium->{method} eq 'cdrom' ? + (map { get_phys_media_cdrom($main_phys_medium, $_) } @other_names) : + (); + + if (@other_phys_media) { + $main_phys_medium->{name} = $main_name; + + my @phys_media = ($main_phys_medium, @other_phys_media); + + foreach my $medium (@$hdlists) { + if (my $phys_m = find { $_->{name} eq $medium->{name} } @phys_media) { + $medium->{phys_medium} = $phys_m; + } else { + $medium->{selected} = 0; + log::l("deselecting missing medium $medium->{rpmsdir}"); + } + } + } else { + foreach my $medium (@$hdlists) { + $medium->{phys_medium} = $main_phys_medium; + } + } +} + +sub get_phys_media_cdrom { + my ($main_phys_m, $name) = @_; + + #- exactly the same as $main_phys_m, but for {name}, {isMounted} and {real_mntpoint} + +{ %$main_phys_m, name => $name, isMounted => 0, real_mntpoint => undef }; +} + +sub iso_phys_media { + my ($dir_medium, $iso, $rel_path) = @_; + + my $mntpoint = "/mnt/$iso"; + $mntpoint =~ s/\.iso$//; #- make the mount point a little nicer + + my $rel_file = $dir_medium->{rel_path} . "/$iso"; + + +{ + url => $dir_medium->{url} . "/$iso", #- only used for printing + method => 'iso', + fs_type => 'iso9660', options => 'noauto,loop', + loopback_device => $dir_medium, loopback_file => $rel_file, + device => ($dir_medium->{real_mntpoint} || $::prefix . $dir_medium->{mntpoint}) . $rel_file, + mntpoint => $mntpoint, rel_path => $rel_path, + }; +} +sub get_phys_media_iso { + my ($all_hds, $main_phys_m, $names) = @_; + + my @ISOs = grep { member($_->{app_id}, @$names) } look_for_ISO_images($main_phys_m->{device}); + + map { + my $m = iso_phys_media($main_phys_m->{loopback_device}, $_->{file}, $main_phys_m->{rel_path}); + $m->{name} = $_->{app_id}; + push @{$all_hds->{loopbacks}}, $m; + $m; + } @ISOs; +} +sub look_for_ISO_images { + my ($main_iso) = @_; + + my $iso_dir = dirname($main_iso); + + my @media = map { + if (sysopen(my $F, "$iso_dir/$_", 0)) { + my ($vol_id, $app_id) = c::get_iso_volume_ids(fileno $F); + #- the ISO volume names must end in -Disc\d+ if they belong (!) to a set + #- otherwise use the full volume name as CD set identifier + my $cd_set = $vol_id =~ /^(.*)-disc\d+$/i ? $1 : $vol_id; + + log::l("found ISO: file=$_ cd_set=$cd_set app_id=$app_id"); + { cd_set => $cd_set, app_id => $app_id, file => $_ }; + } else { + (); + } + } grep { /\.iso$/ } all($iso_dir); + + my $main = find { basename($main_iso) eq $_->{file} } @media or return; + + grep { $_->{cd_set} eq $main->{cd_set} } @media; +} + + +sub getFile_media_info { + my ($packages, $f) = @_; + getFile_(first_medium($packages)->{phys_medium}, $f); +} + +sub open_file_and_size { + my ($f) = @_; + my $size = -s $f; + my $fh = common::open_file($f) or return; + $size, $fh; +} +sub getFile_ { + my ($phys_m, $f) = @_; + log::l("getFile $f on " . phys_medium_to_string($phys_m) . ""); + + my ($_size, $fh) = get_file_and_size($phys_m, $f) or return; + $fh; +} +sub get_file_and_size { + my ($phys_m, $f) = @_; + + if ($f =~ m|^http://|) { + require install::http; + install::http::get_file_and_size($f); + } elsif ($phys_m->{method} eq "ftp") { + require install::ftp; + install::ftp::get_file_and_size($f, $phys_m->{url}); + } elsif ($phys_m->{method} eq "http") { + require install::http; + install::http::get_file_and_size("$phys_m->{url}/$f"); + } elsif ($f =~ m!^/!) { + open_file_and_size($f); + } elsif ($postinstall_rpms && -e "$postinstall_rpms/$f") { + open_file_and_size("$postinstall_rpms/$f"); + } else { + my $f2 = path($phys_m, $f); + + if (! -f $f2) { + change_phys_medium($phys_m, $f); + } + open_file_and_size($f2); + } +} + +sub getAndSaveFile_ { + my ($phys_m, $file, $local) = @_; + my $fh = getFile_($phys_m, $file) or return; + getAndSaveFile_raw($fh, $local); +} +sub getAndSaveFile_progress { + my ($in_wait, $msg, $phys_m, $file, $local) = @_; + my ($size, $fh) = get_file_and_size($phys_m, $file) or return; + if ($size) { + getAndSaveFile_progress_raw($in_wait, $msg, $size, $fh, $local); + } else { + getAndSaveFile_raw($fh, $local); + } +} +sub getAndSaveFile_raw { + my ($fh, $local) = @_; + + local $/ = \ (16 * 1024); + unlink $local; + open(my $F, ">$local") or log::l("getAndSaveFile(opening $local): $!"), return; + local $_; + while (<$fh>) { syswrite($F, $_) or unlink($local), die("getAndSaveFile($local): $!") } + 1; +} +sub getAndSaveFile_progress_raw { + my ($in_wait, $msg, $size, $fh, $local) = @_; + + unlink $local; + open(my $out, ">$local") or log::l("getAndSaveFile(opening $local): $!"), return; + print_with_progress($in_wait, $msg, $size, $fh, $out) or unlink($local), die("getAndSaveFile($local): $!"); +} +sub print_with_progress { + my ($in_wait, $msg, $size, $in, $out) = @_; + + my ($_wait, $wait_message) = $in_wait->wait_message_with_progress_bar(N("Please wait")); + $wait_message->($msg); + + my $current = 0; + + require Time::HiRes; + my $time = Time::HiRes::time(); + + local $/ = \ (64 * 1024); + while (my $s = <$in>) { + syswrite($out, $s) or return; + + $current += length($s); + if (Time::HiRes::time() > $time + 0.1) { + $wait_message->('', $current, $size); + $time = Time::HiRes::time(); + } + } + 1; +} + + +sub urpmidir() { + my $v = "$::prefix/var/lib/urpmi"; + -l $v && !-e $v and unlink $v and mkdir $v, 0755; #- dangling symlink + -w $v ? $v : '/tmp'; +} + +sub hdlist_on_disk { + my ($m) = @_; + + urpmidir() . "/hdlist.$m->{fakemedium}.cz"; +} + +sub allow_copy_rpms_on_disk { + my ($medium, $hdlists) = @_; + + $medium->{device} && $medium->{method} ne 'iso' or return; + + #- check available size for copying rpms from infos in media.cfg file + my $totalsize = sum(map { $_->{size} } @$hdlists) || -1; #- don't check size, total medium size unknown + + if ($totalsize >= 0) { + my $availvar = install::any::getAvailableSpace_mounted("$::prefix/var"); + $availvar /= 1024 * 1024; #- Mo + log::l("totalsize=$totalsize, avail on $::prefix/var=$availvar"); + $totalsize < $availvar * 0.6; + } else { + #- we hope it will fit... + 1; + } +} + +sub parse_media_cfg { + my ($cfg) = @_; + + require MDV::Distribconf; + my $d = MDV::Distribconf->new('', undef); + $d->parse_mediacfg($cfg); + + my $distribconf = { map { $_ => $d->getvalue(undef, $_) } 'suppl', 'askmedia' }; + my @hdlists = map { + my ($size) = $d->getvalue($_, 'size') =~ /(\d+)MB?/i; + { + rpmsdir => $_, + hdlist => $d->getvalue($_, 'hdlist'), + name => $d->getvalue($_, 'name'), + size => $size, + selected => !$d->getvalue($_, 'noauto'), + update => $d->getvalue($_, 'updates_for') ? 1 : undef, + }; + } $d->listmedia; + + $distribconf, \@hdlists; +} + +sub parse_hdlists { + my ($cfg) = @_; + + my (%main_options, @hdlists); + foreach (cat_($cfg)) { + chomp; + s/\s*#.*$//; + /^\s*$/ and next; + #- we'll ask afterwards for supplementary CDs, if the hdlists file contains + #- a line that begins with "suppl" + if (/^suppl/) { $main_options{suppl} = 1; next } + #- if the hdlists contains a line "askmedia", deletion of media found + #- in this hdlist is allowed + if (/^askmedia/) { $main_options{askmedia} = 1; next } + my ($noauto, $hdlist, $rpmsdir, $name, $size) = m!^\s*(noauto:)?(hdlist\S*\.cz)\s+[^/]*/(\S+)\s*([^(]*)(?:\((.+)\))?$! + or die qq(invalid hdlist description "$_" in hdlists file); + $name =~ s/\s+$//; + $size =~ s/MB?$//i; + push @hdlists, { hdlist => $hdlist, rpmsdir => $rpmsdir, name => $name, selected => !$noauto, size => $size }; + } + (\%main_options, \@hdlists); +} + +sub get_media { + my ($o, $media, $packages) = @_; + + my ($suppl_CDs, $copy_rpms_on_disk); + foreach (@$media) { + if ($_->{type} eq 'media_cfg') { + my $phys_m = url2mounted_phys_medium($o, $_->{url}, 'media_info'); + ($suppl_CDs, $copy_rpms_on_disk) = get_media_cfg($o, $phys_m, $packages, $_->{selected_names}, $_->{force_rpmsrate}); + } elsif ($_->{type} eq 'media') { + my $phys_m = url2mounted_phys_medium($o, $_->{url}); + get_standalone_medium($o, $phys_m, $packages, { name => $_->{id} =~ /media=(.*)/ && $1 }); + } elsif ($_->{type} eq 'media_cfg_isos') { + my ($dir_url, $iso, $rel_path) = $_->{url} =~ m!(.*)/(.*\.iso):(/.*)!; + my $dir_medium = url2mounted_phys_medium($o, $dir_url); + $dir_medium->{options} =~ s/\bnoauto\b,?//; + my $phys_m = iso_phys_media($dir_medium, $iso, $rel_path); + push @{$o->{all_hds}{loopbacks}}, $phys_m; + ($suppl_CDs, $copy_rpms_on_disk) = get_media_cfg($o, $phys_m, $packages, $_->{selected_names}, $_->{force_rpmsrate}); + } else { + log::l("unknown media type $_->{type}, skipping"); + } + } + log::l("suppl_CDs=$suppl_CDs copy_rpms_on_disk=$copy_rpms_on_disk"); + $suppl_CDs, $copy_rpms_on_disk; +} + +sub remove_from_fstab { + my ($all_hds, $phys_m) = @_; + + @{$all_hds->{nfss}} = grep { $_ != $phys_m } @{$all_hds->{nfss}} if $phys_m->{method} eq 'nfs'; +} + +sub find_and_add_to_fstab { + my ($all_hds, $phys_m, $b_force_mount) = @_; + + if (my $existant = find { $_->{device} eq $phys_m->{device} } fs::get::really_all_fstab($all_hds)) { + add2hash($existant, $phys_m); + $phys_m = $existant; + } else { + push @{$all_hds->{nfss}}, $phys_m if $phys_m->{method} eq 'nfs'; + push @{$all_hds->{loopbacks}}, $phys_m if isLoopback($phys_m); + } + + if (!$phys_m->{mntpoint}) { + my @suggestions = $phys_m->{method} eq 'nfs' ? do { + my ($server) = $phys_m->{device} =~ /(.*?):/; + $phys_m->{options} = ($b_force_mount ? '' : 'noauto,') . 'ro,nosuid,soft,rsize=8192,wsize=8192'; + '/mnt/nfs', "/mnt/nfs_$server"; + } : $phys_m->{method} eq 'cdrom' ? + ('/media/cdrom', "/media/$phys_m->{device}") : + ('/mnt/hd', "/mnt/$phys_m->{device}"); + + my $last = $suggestions[-1]; + push @suggestions, map { "$last$_" } 2 .. 30; + $phys_m->{mntpoint} = find { !fs::get::has_mntpoint($_, $all_hds) } @suggestions or internal_error("no free dir available"); + } + $phys_m; +} + +sub url2mounted_phys_medium { + my ($o, $url, $o_rel_file, $o_name) = @_; + + my $phys_m = url2phys_medium($o, $url); + $phys_m->{name} = $o_name if $o_name; #- useful for CDs which prompts a name in change_phys_medium + change_phys_medium($phys_m, $o_rel_file) or return; + $phys_m; +} + +sub url2phys_medium { + my ($o, $url) = @_; + my ($method, $path) = $url =~ m!([^:]*)://(.*)! or internal_error("bad url $url"); + if ($method eq 'drakx') { + my $m = { %{$o->{stage2_phys_medium}}, is_stage2_phys_medium => 1 }; + if ($m->{loopback_device}) { + $m->{loopback_device} = find_and_add_to_fstab($o->{all_hds}, $m->{loopback_device}, 'force_mount'); + } + $m->{url} .= "/$path"; + $m->{rel_path} .= "/$path" if $m->{device}; + $m = find_and_add_to_fstab($o->{all_hds}, $m) if $m->{device}; + phys_medium_is_mounted($m); + $m; + } elsif ($method eq 'cdrom') { + my $cdrom = first(detect_devices::cdroms()); + my $m = { + url => $url, method => $method, fs_type => 'iso9660', device => $cdrom->{device}, + rel_path => "/$path", + }; + my $m_ = find_and_add_to_fstab($o->{all_hds}, $m); + if ($m_->{name}) { + #- we need a new phys medium, different from current CD + $m_ = get_phys_media_cdrom($m_, ''); + #- we also need to enforce what we want, especially rel_path + put_in_hash($m_, $m); + } + $m_; + } elsif ($method eq 'nfs') { + my ($server, $nfs_dir) = $path =~ m!(.*?)(/.*)!; + + my $m = { + url => $url, method => $method, + fs_type => 'nfs', device => "$server:$nfs_dir", faked_device => 1, + }; + find_and_add_to_fstab($o->{all_hds}, $m); + } else { + { url => $url, method => $method }; + } +} + +sub get_media_cfg { + my ($o, $phys_medium, $packages, $selected_names, $force_rpmsrate) = @_; + + my ($distribconf, $hdlists); + if (getAndSaveFile_($phys_medium, 'media_info/media.cfg', '/tmp/media.cfg')) { + ($distribconf, $hdlists) = parse_media_cfg('/tmp/media.cfg'); + } else { + getAndSaveFile_($phys_medium, 'media_info/hdlists', '/tmp/hdlists') + or die "media.cfg not found"; + ($distribconf, $hdlists) = parse_hdlists('/tmp/hdlists'); + } + + if (defined $selected_names) { + my @names = split ',', $selected_names; + foreach my $h (@$hdlists) { + $h->{selected} = member($h->{name}, @names); + } + } + + my $suppl_CDs = $distribconf->{suppl} || $o->{supplmedia} || 0; + my $deselectionAllowed = $distribconf->{askmedia} || $o->{askmedia} || 0; + + associate_phys_media($o->{all_hds}, $phys_medium, $hdlists); + + if ($deselectionAllowed && !@{$packages->{mediums}}) { + my $allow = allow_copy_rpms_on_disk($phys_medium, $hdlists); + $o->ask_deselect_media__copy_on_disk($hdlists, $allow && \$o->{copy_rpms_on_disk}) if $allow || @$hdlists > 1; + } + + foreach my $h (@$hdlists) { + get_medium($o, $phys_medium, $packages, $h); + } + + log::l("get_media_cfg read " . int(@{$packages->{depslist}}) . " headers"); + + + #- copy latest compssUsers.pl and rpmsrate somewhere locally + if ($force_rpmsrate || ! -e '/tmp/rpmsrate') { + getAndSaveFile_($phys_medium, "media_info/compssUsers.pl", "/tmp/compssUsers.pl"); + getAndSaveFile_($phys_medium, "media_info/rpmsrate", "/tmp/rpmsrate"); + } + + + $suppl_CDs, $o->{copy_rpms_on_disk}; +} + +sub get_standalone_medium { + my ($in, $phys_m, $packages, $m) = @_; + + add2hash($m, { phys_medium => $phys_m, selected => 1, hdlist => 'hdlist.cz' }); + get_medium($in, $phys_m, $packages, $m); +} + +sub get_medium { + my ($in_wait, $phys_m, $packages, $m) = @_; + + $m->{selected} or log::l("ignoring packages in $m->{hdlist}"), return; + + my $medium_id = int @{$packages->{mediums}}; + $m->{fakemedium} = $m->{name} || $phys_m->{method}; + $m->{fakemedium} =~ s!/!_!g; #- remove "/" from name + if (find { $m->{fakemedium} eq $_->{fakemedium} } allMediums($packages)) { + $m->{fakemedium} .= " (" . ($m->{rpmsdir} || $medium_id) . ")"; + $m->{fakemedium} =~ s!/!_!g; #- remove "/" from rpmsdir + } + + log::l("trying to read $m->{hdlist} for medium '$m->{fakemedium}'"); + + #- copy hdlist file directly to urpmi directory, this will be used + #- for getting header of package during installation or after by urpmi. + my $hdlist = hdlist_on_disk($m); + { + getAndSaveFile_progress($in_wait, N("Downloading file %s...", $m->{hdlist}), + $phys_m, "media_info/$m->{hdlist}", $hdlist) or die "no $m->{hdlist} found"; + + $m->{hdlist_size} = -s $hdlist; #- keep track of size for post-check. + } + + my $synthesis = urpmidir() . "/synthesis.hdlist.$m->{fakemedium}.cz"; + { + #- copy existing synthesis file too. + getAndSaveFile_progress($in_wait, N("Downloading file %s...", "synthesis.$m->{hdlist}"), + $phys_m, "media_info/synthesis.$m->{hdlist}", $synthesis); + $m->{synthesis_hdlist_size} = -s $synthesis; #- keep track of size for post-check. + } + + #- get all keys corresponding in the right pubkey file, + #- they will be added in rpmdb later if not found. + if (!$m->{pubkey}) { + if (my $pubkey = getFile_($phys_m, "media_info/pubkey" . ($m->{hdlist} =~ /hdlist(\S*)\.cz/ && $1))) { + $m->{pubkey} = [ $packages->parse_armored_file($pubkey) ]; + } + } + $m->{pubkey} ||= []; + + #- for standalone medium not using media.cfg + $phys_m->{name} ||= $m->{name}; + + #- integrate medium in media list, only here to avoid download error (update) to be propagated. + push @{$packages->{mediums}}, $m; + + #- parse synthesis (if available) of directly hdlist (with packing). + { + my $nb_suppl_pkg_skipped = 0; + my $callback = sub { + my (undef, $p) = @_; + my $uniq_pkg_seen = $packages->{uniq_pkg_seen} ||= {}; + if ($uniq_pkg_seen->{$p->fullname}++) { + log::l("skipping " . scalar $p->fullname); + ++$nb_suppl_pkg_skipped; + return 0; + } else { + return 1; + } + }; + my $error; + if (-s $synthesis) { + ($m->{start}, $m->{end}) = $packages->parse_synthesis($synthesis, callback => $callback) + or $error = "bad synthesis $synthesis for $m->{fakemedium}"; + } elsif (-s $hdlist) { + ($m->{start}, $m->{end}) = $packages->parse_hdlist($hdlist, callback => $callback) + or $error = "bad hdlist $hdlist for $m->{fakemedium}"; + } else { + $error = "fatal: no hdlist nor synthesis to read for $m->{fakemedium}"; + } + + if ($error) { + pop @{$packages->{mediums}}; + unlink $hdlist, $synthesis; + die $error; + } else { + log::l("medium " . phys_medium_to_string($phys_m) . ", read " . ($m->{end} - $m->{start} + 1) . " packages in $m->{hdlist}, $nb_suppl_pkg_skipped skipped"); + } + } +} + + + +#-###################################################################################### +#- Post installation RPMS from cdrom only, functions +#-###################################################################################### +sub setup_postinstall_rpms { + my ($in, $packages, $current_phys_m) = @_; + + $postinstall_rpms and return; + $postinstall_rpms = "$::prefix/usr/postinstall-rpm"; + + log::l("postinstall rpms directory set to $postinstall_rpms"); + clean_postinstall_rpms(); #- make sure in case of previous upgrade problem. + + my @toCopy; + { + #- compute closure of package that may be copied, use INSTALL category + #- in rpmsrate. + @toCopy = install::pkgs::select_by_package_names($packages, $packages->{needToCopy} || []); + log::l("needToCopy the following packages: " . join(' ', map { $_->name } @toCopy)); + $packages->disable_selected($packages->{rpmdb}, $packages->{state}, @toCopy); + delete $packages->{rpmdb}; + } + + my $medium = find { $_->{phys_medium} == $current_phys_m } allMediums($packages); + + my @l = map { path($current_phys_m, "$medium->{rpmsdir}/" . $_->filename) } @toCopy; + + my ($l, $missing) = partition { -r $_ } @l; + + @$missing and log::l("rpms not available: " . join(' ', @$missing)); + + #- copy the package files in the postinstall RPMS directory. + #- cp_af does not handle correctly a missing file. + mkdir_p("$postinstall_rpms/$medium->{rpmsdir}"); + eval { + my ($_w, $wait_message) = $in->wait_message_with_progress_bar; + $wait_message->(N("Copying some packages on disks for future use")); + install::any::cp_with_progress($wait_message, 0, int(@$l), @$l, "$postinstall_rpms/$medium->{rpmsdir}"); + }; + !$@ or log::l("copying to postinstall dir failed: $@"); + + log::l("copying Auto Install Floppy"); + getAndSaveInstallFloppies($::o, $postinstall_rpms, 'auto_install'); +} + +sub getAndSaveInstallFloppies { + my ($o, $dest_dir, $name) = @_; + + if ($postinstall_rpms && -d $postinstall_rpms && -r "$postinstall_rpms/auto_install.img") { + log::l("getAndSaveInstallFloppies: using file saved as $postinstall_rpms/auto_install.img"); + cp_af("$postinstall_rpms/auto_install.img", "$dest_dir/$name.img"); + "$dest_dir/$name.img"; + } else { + my $image = 'hd_grub'; + + getAndSaveFile_($o->{stage2_phys_medium}, "install/images/$image.img", "$dest_dir/$name.img") + or log::l("failed to write Install Floppy ($image.img) to $dest_dir/$name.img"), return; + + "$dest_dir/$name.img"; + } +} + +sub clean_postinstall_rpms() { + if ($postinstall_rpms && -d $postinstall_rpms) { + rm_rf($postinstall_rpms); + } +} + +sub copy_rpms_on_disk { + my ($o) = @_; + + my $dest_dir = '/var/ftp/pub/Mandrivalinux/media'; + #- don't be afraid, cleanup old RPMs if upgrade + eval { rm_rf("$::prefix$dest_dir") if $o->{isUpgrade} }; + mkdir_p("$::prefix$dest_dir"); + + my $dest_phys_medium = do { + my ($part, $rel_path) = fs::get::file2part($o->{fstab}, $dest_dir); + $part->{method} = 'disk'; + $part->{rel_path} = $rel_path; + $part->{url} = "disk://$part->{device}$rel_path"; + $part; + }; + + my ($wait, $wait_message) = $o->wait_message_with_progress_bar; + + foreach my $m (allMediums($o->{packages})) { + #- don't copy rpms of supplementary media + next if $m->{phys_medium}{is_suppl}; + $wait_message->(N("Copying in progress") . "\n($m->{name})"); #- XXX to be translated + my $rpmsdir = path($m->{phys_medium}, $m->{rpmsdir}); + if (! -d $rpmsdir) { + if (!change_phys_medium($m->{phys_medium}, $m->{rpmsdir})) { + #- keep in mind the asked medium has been refused. + #- this means it is no longer selected. + #- (but do not unselect supplementary CDs.) + $m->{selected} = 0; + } + } + + my $total = install::any::count_files($rpmsdir); + log::l("copying $rpmsdir to $::prefix$dest_dir ($total files)"); + eval { + install::any::cp_with_progress($wait_message, 0, $total, $rpmsdir, "$::prefix$dest_dir"); + }; + log::l($@) if $@; + + $m->{phys_medium} = $dest_phys_medium; + } + undef $wait; + + our $copied_rpms_on_disk = 1; +} + +sub install_urpmi__generate_names { + my ($packages, $medium) = @_; + + #- build a names file + output("$::prefix/var/lib/urpmi/names.$medium->{fakemedium}", + map { $packages->{depslist}[$_]->name . "\n" } $medium->{start} .. $medium->{end}); +} +sub install_urpmi__generate_synthesis { + my ($packages, $medium) = @_; + + my $synthesis = "/var/lib/urpmi/synthesis.hdlist.$medium->{fakemedium}.cz"; + + #- build synthesis file if there are still not existing (ie not copied from mirror). + -s "$::prefix$synthesis" <= 32 or return; + + log::l("building $synthesis"); + + eval { $packages->build_synthesis( + start => $medium->{start}, + end => $medium->{end}, + synthesis => "$::prefix$synthesis", + ) }; + $@ and log::l("build_synthesis failed: $@"); +} + +#- copied from urpm/media.pm +sub parse_url_with_login { + my ($url) = @_; + $url =~ m!([^:]*)://([^/:\@]*)(:([^/:\@]*))?\@([^/]*)(.*)! && + { proto => $1, login => $2, password => $4, machine => $5, dir => $6 }; +} + +sub install_urpmi { + my ($stage2_method, $packages) = @_; + + my @mediums = @{$packages->{mediums}}; + + log::l("install_urpmi $stage2_method"); + #- clean to avoid opening twice the rpm db. + delete $packages->{rpmdb}; + + #- import pubkey in rpmdb. + my $db = install::pkgs::open_rpm_db_rw(); + $packages->parse_pubkeys(db => $db); + foreach my $medium (@mediums) { + $packages->import_needed_pubkeys($medium->{pubkey}, db => $db, callback => sub { + my (undef, undef, $_k, $id, $imported) = @_; + if ($id) { + log::l(($imported ? "imported" : "found") . " key=$id for medium $medium->{name}"); + $medium->{key_ids}{$id} = undef; + } + }); + } + + my (@cfg, @netrc); + foreach my $medium (@mediums) { + if ($medium->{selected}) { + my ($dir, $removable_device, $static); + + my $phys_m = $medium->{phys_medium}; + if ($phys_m->{method} eq 'ftp' || $phys_m->{method} eq 'http') { + $dir = $phys_m->{url}; + } else { + #- for cdrom, removable://... is best since it mounts *and* umounts cdrom + #- for iso files, removable://... doesn't work correctly + my $urpmi_method = $phys_m->{method} eq 'cdrom' ? 'removable' : 'file'; + $dir = "$urpmi_method:/$phys_m->{mntpoint}$phys_m->{rel_path}"; + if ($phys_m->{method} eq 'iso') { + $removable_device = $phys_m->{loopback_device}{mntpoint} . $phys_m->{loopback_file}; + } elsif ($phys_m->{method} eq 'cdrom') { + $removable_device = devices::make($phys_m->{device}); + $static = 1; + } + } + + $dir = MDK::Common::File::concat_symlink($dir, $medium->{rpmsdir}); + + install_urpmi__generate_names($packages, $medium); + install_urpmi__generate_synthesis($packages, $medium); + + my ($qname, $qdir) = ($medium->{fakemedium}, $dir); + + if (my $u = parse_url_with_login($qdir)) { + $qdir = sprintf('%s://%s@%s%s', $u->{proto}, $u->{login}, $u->{machine}, $u->{dir}); + push @netrc, sprintf("machine %s login %s password %s\n", $u->{machine}, $u->{login}, $u->{password}); + } + + s/(\s)/\\$1/g foreach $qname, $qdir; + + #- output new urpmi.cfg format here. + push @cfg, map { "$_\n" } + "$qname $qdir {", + " media_info_dir: media_info", + if_(keys(%{$medium->{key_ids}}), + " key-ids: " . join(',', keys %{$medium->{key_ids}})), + if_($removable_device, + " removable: $removable_device"), + if_($medium->{update}, + " update"), + if_($static, + " static"), + "}"; + } else { + #- remove deselected media by removing copied hdlist and synthesis files + log::l("removing media $medium->{fakemedium}"); + unlink "$::prefix/var/lib/urpmi/hdlist.$medium->{fakemedium}.cz"; + unlink "$::prefix/var/lib/urpmi/synthesis.hdlist.$medium->{fakemedium}.cz"; + } + } + eval { output("$::prefix/etc/urpmi/netrc", @netrc) }; + #- touch a MD5SUM file and write config file + eval { output("$::prefix/var/lib/urpmi/MD5SUM", '') }; + eval { output "$::prefix/etc/urpmi/urpmi.cfg", @cfg }; +} + + +sub openCdromTray { + my ($cdrom) = @_; + log::l("ejecting cdrom $cdrom"); + eval { ioctl(detect_devices::tryOpen($cdrom), c::CDROMEJECT(), 1) }; + $@ and log::l("ejection failed: $@"); +} + +sub log_sizes() { + my @df = MDK::Common::System::df($::prefix); + log::l(sprintf "Installed: %dMB(df), %dMB(rpm)", + ($df[0] - $df[1]) / 1024, + sum(run_program::rooted_get_stdout($::prefix, 'rpm', '-qa', '--queryformat', '%{size}\n')) / 1024 / 1024) if -x "$::prefix/bin/rpm"; +} + +1; |