1 |
package urpm::get_pkgs; |
2 |
|
3 |
# $Id: get_pkgs.pm 267986 2010-04-28 10:10:27Z cfergeau $ |
4 |
|
5 |
use strict; |
6 |
use urpm::msg; |
7 |
use urpm::sys; |
8 |
use urpm::util; |
9 |
use urpm::media; |
10 |
use urpm 'file_from_local_url'; |
11 |
# perl_checker: require urpm::select |
12 |
|
13 |
sub clean_all_cache { |
14 |
my ($urpm) = @_; |
15 |
#- clean download directory, do it here even if this is not the best moment. |
16 |
$urpm->{log}(N("cleaning %s and %s", "$urpm->{cachedir}/partial", "$urpm->{cachedir}/rpms")); |
17 |
urpm::sys::empty_dir("$urpm->{cachedir}/partial"); |
18 |
urpm::sys::empty_dir("$urpm->{cachedir}/rpms"); |
19 |
} |
20 |
|
21 |
sub cachedir_rpms { |
22 |
my ($urpm) = @_; |
23 |
|
24 |
#- examine the local repository, which is trusted (no gpg or pgp signature check but md5 is now done). |
25 |
my %fn2file; |
26 |
foreach my $filepath (glob("$urpm->{cachedir}/rpms/*")) { |
27 |
next if -d $filepath; |
28 |
|
29 |
if (! -s $filepath) { |
30 |
unlink $filepath; #- this file should be removed or is already empty. |
31 |
} else { |
32 |
my $filename = basename($filepath); |
33 |
my ($fullname) = $filename =~ /(.*)\.rpm$/ or next; |
34 |
$fn2file{$fullname} = $filepath; |
35 |
} |
36 |
} |
37 |
\%fn2file; |
38 |
} |
39 |
|
40 |
#- select sources for selected packages, |
41 |
#- according to keys of the packages hash. |
42 |
#- returns a list of lists containing the source description for each rpm, |
43 |
#- matching the exact number of registered media; ignored media being |
44 |
#- associated to a null list. |
45 |
sub _selected2local_and_ids { |
46 |
my ($urpm, $packages, %options) = @_; |
47 |
my (%protected_files, %local_sources, %fullname2id); |
48 |
|
49 |
#- build association hash to retrieve id and examine all list files. |
50 |
foreach (keys %$packages) { |
51 |
foreach my $id (split /\|/, $_) { |
52 |
if ($urpm->{source}{$_}) { |
53 |
my $file = $local_sources{$id} = $urpm->{source}{$id}; |
54 |
$protected_files{$file} = undef; |
55 |
} else { |
56 |
$fullname2id{$urpm->{depslist}[$id]->fullname} = $id; |
57 |
} |
58 |
} |
59 |
} |
60 |
|
61 |
#- examine the local repository, which is trusted (no gpg or pgp signature check but md5 is now done). |
62 |
my $cachedir_rpms = cachedir_rpms($urpm); |
63 |
|
64 |
foreach my $fullname (keys %$cachedir_rpms) { |
65 |
my $filepath = $cachedir_rpms->{$fullname}; |
66 |
|
67 |
if (my $id = delete $fullname2id{$fullname}) { |
68 |
$local_sources{$id} = $filepath; |
69 |
} else { |
70 |
$options{clean_other} && ! exists $protected_files{$filepath} and unlink $filepath; |
71 |
} |
72 |
} |
73 |
|
74 |
my %id2ids; |
75 |
foreach my $id (values %fullname2id) { |
76 |
my $pkg = $urpm->{depslist}[$id]; |
77 |
my $fullname = $pkg->fullname; |
78 |
my @pkg_ids = $pkg->arch eq 'src' ? do { |
79 |
# packages_by_name can't be used here since $urpm->{provides} doesn't have src.rpm |
80 |
# so a full search is needed |
81 |
my %requested; |
82 |
urpm::select::search_packages($urpm, \%requested, [$pkg->name], src => 1); |
83 |
map { split /\|/ } keys %requested; |
84 |
} : do { |
85 |
map { $_->id } grep { |
86 |
$_->filename !~ /\.delta\.rpm$/ || $urpm->is_delta_installable($_, $urpm->{root}); |
87 |
} grep { $fullname eq $_->fullname } $urpm->packages_by_name($pkg->name); |
88 |
}; |
89 |
|
90 |
$id2ids{$id} = \@pkg_ids; |
91 |
} |
92 |
|
93 |
(\%local_sources, \%id2ids); |
94 |
} |
95 |
|
96 |
sub selected2local_and_blists { |
97 |
my ($urpm, $selected, %options) = @_; |
98 |
|
99 |
my ($local_sources, $id2ids) = _selected2local_and_ids($urpm, $selected, %options); |
100 |
|
101 |
# id_map is a remapping of id. |
102 |
# it is needed because @list must be [ { id => pkg } ] where id is one the selected id, |
103 |
# not really the real package id |
104 |
my %id_map; |
105 |
foreach my $id (keys %$id2ids) { |
106 |
$id_map{$_} = $id foreach @{$id2ids->{$id}}; |
107 |
} |
108 |
|
109 |
my @remaining_ids = sort { $a <=> $b } keys %id_map; |
110 |
|
111 |
my %blists; |
112 |
my @blists = map { |
113 |
my $medium = $_; |
114 |
my %pkgs; |
115 |
if (urpm::media::is_valid_medium($medium) && !$medium->{ignore}) { |
116 |
while (@remaining_ids) { |
117 |
my $id = $remaining_ids[0]; |
118 |
$medium->{start} <= $id && $id <= $medium->{end} or last; |
119 |
shift @remaining_ids; |
120 |
|
121 |
my $maped_id = $id_map{$id}; |
122 |
# no duplicate package (especially noarch ones, eg from 32 & 64 bit media): |
123 |
next if $blists{$maped_id}; |
124 |
$blists{$maped_id} = 1; |
125 |
my $pkg = $urpm->{depslist}[$id]; |
126 |
$pkgs{$maped_id} = $pkg; |
127 |
} |
128 |
} |
129 |
%pkgs ? { medium => $medium, pkgs => \%pkgs } : (); |
130 |
} (@{$urpm->{media} || []}); |
131 |
|
132 |
if (@remaining_ids) { |
133 |
$urpm->{error}(N("package %s is not found.", $urpm->{depslist}[$_]->fullname)) foreach @remaining_ids; |
134 |
return; |
135 |
} |
136 |
|
137 |
($local_sources, \@blists); |
138 |
} |
139 |
|
140 |
#- side-effects: none |
141 |
sub _create_old_list_from_blists { |
142 |
my ($media, $blists) = @_; |
143 |
|
144 |
[ map { |
145 |
my $medium = $_; |
146 |
my ($blist) = grep { $_->{medium} == $medium } @$blists; |
147 |
|
148 |
{ map { $_ => urpm::blist_pkg_to_url($blist, $blist->{pkgs}{$_}) } keys %{$blist->{pkgs}} } |
149 |
} @$media ]; |
150 |
} |
151 |
|
152 |
# deprecated, use selected2local_and_blists() instead |
153 |
sub selected2list { |
154 |
my ($urpm, $selected, %options) = @_; |
155 |
|
156 |
my ($local_sources, $blists) = selected2local_and_blists($urpm, $selected, %options); |
157 |
($local_sources, _create_old_list_from_blists($urpm->{media}, $blists)); |
158 |
} |
159 |
|
160 |
sub verify_partial_rpm_and_move { |
161 |
my ($urpm, $cachedir, $filename) = @_; |
162 |
|
163 |
URPM::verify_rpm("$cachedir/partial/$filename", nosignatures => 1) or do { |
164 |
unlink "$cachedir/partial/$filename"; |
165 |
return; |
166 |
}; |
167 |
#- it seems the the file has been downloaded correctly and has been checked to be valid. |
168 |
unlink "$cachedir/rpms/$filename"; |
169 |
urpm::sys::move_or_die($urpm, "$cachedir/partial/$filename", "$cachedir/rpms/$filename"); |
170 |
"$cachedir/rpms/$filename"; |
171 |
} |
172 |
|
173 |
# get the filesize of packages to download from remote media. |
174 |
sub get_distant_media_filesize { |
175 |
my (undef, $blists, $sources) = @_; |
176 |
|
177 |
my $filesize; |
178 |
#- get back all ftp and http accessible rpm files into the local cache |
179 |
foreach my $blist (@$blists) { |
180 |
#- examine all files to know what can be indexed on multiple media. |
181 |
while (my ($id, $pkg) = each %{$blist->{pkgs}}) { |
182 |
#- the given URL is trusted, so the file can safely be ignored. |
183 |
defined $sources->{$id} and next; |
184 |
if (!urpm::is_local_medium($blist->{medium})) { |
185 |
if (my $n = $pkg->filesize) { |
186 |
$filesize += $n; |
187 |
} |
188 |
} |
189 |
} |
190 |
} |
191 |
$filesize; |
192 |
} |
193 |
|
194 |
# download packages listed in $blists, |
195 |
# and put the result in $sources or $error_sources |
196 |
# |
197 |
#- options: quiet, callback, |
198 |
sub download_packages_of_distant_media { |
199 |
my ($urpm, $blists, $sources, $error_sources, %options) = @_; |
200 |
|
201 |
my %errors; |
202 |
my %new_sources; |
203 |
|
204 |
#- get back all ftp and http accessible rpm files into the local cache |
205 |
foreach my $blist (@$blists) { |
206 |
my %blist_distant = (%$blist, pkgs => {}); |
207 |
|
208 |
#- examine all files to know what can be indexed on multiple media. |
209 |
while (my ($id, $pkg) = each %{$blist->{pkgs}}) { |
210 |
#- the given URL is trusted, so the file can safely be ignored. |
211 |
if (defined $sources->{$id}) { |
212 |
$new_sources{$id} = [ $pkg->id, $sources->{$id} ]; |
213 |
delete $sources->{$id}; |
214 |
next; |
215 |
} |
216 |
|
217 |
exists $new_sources{$id} and next; |
218 |
if (urpm::is_local_medium($blist->{medium})) { |
219 |
my $local_file = file_from_local_url(urpm::blist_pkg_to_url($blist, $pkg)); |
220 |
if (-r $local_file) { |
221 |
$new_sources{$id} = [ $pkg->id, $local_file ]; |
222 |
} else { |
223 |
$errors{$id} = [ $local_file, 'missing' ]; |
224 |
} |
225 |
} else { |
226 |
$blist_distant{pkgs}{$id} = $pkg; |
227 |
} |
228 |
} |
229 |
|
230 |
if (%{$blist_distant{pkgs}}) { |
231 |
my ($remote_sources, $remote_errors) = _download_packages_of_distant_media($urpm, \%blist_distant, %options); |
232 |
put_in_hash(\%new_sources, $remote_sources); |
233 |
put_in_hash(\%errors, $remote_errors); |
234 |
} |
235 |
} |
236 |
|
237 |
#- clean failed download which have succeeded. |
238 |
delete @errors{keys %$sources, keys %new_sources}; |
239 |
|
240 |
foreach (values %new_sources) { |
241 |
my ($id, $local_file) = @$_; |
242 |
$sources->{$id} = $local_file; |
243 |
} |
244 |
|
245 |
push @$error_sources, values %errors; |
246 |
|
247 |
1; |
248 |
} |
249 |
|
250 |
# download packages listed in $blist, |
251 |
# and put the result in $sources or $errors |
252 |
sub _download_packages_of_distant_media { |
253 |
my ($urpm, $blist, %options) = @_; |
254 |
|
255 |
my $cachedir = urpm::valid_cachedir($urpm); |
256 |
my (%sources, %errors); |
257 |
|
258 |
$urpm->{log}(N("retrieving rpm files from medium \"%s\"...", $blist->{medium}{name})); |
259 |
if (urpm::download::sync_rel($urpm, $blist->{medium}, [ urpm::blist_to_filenames($blist) ], |
260 |
dir => "$cachedir/partial", quiet => $options{quiet}, |
261 |
is_versioned => 1, |
262 |
resume => $urpm->{options}{resume}, |
263 |
ask_retry => $options{ask_retry}, |
264 |
callback => $options{callback})) { |
265 |
$urpm->{log}(N("...retrieving done")); |
266 |
} else { |
267 |
$urpm->{error}(N("...retrieving failed: %s", $@)); |
268 |
} |
269 |
|
270 |
#- clean files that have not been downloaded, but keep in mind |
271 |
#- there have been problems downloading them at least once, this |
272 |
#- is necessary to keep track of failing downloads in order to |
273 |
#- present the error to the user. |
274 |
foreach my $id (keys %{$blist->{pkgs}}) { |
275 |
my $pkg = $blist->{pkgs}{$id}; |
276 |
my $filename = $pkg->filename; |
277 |
my $url = urpm::blist_pkg_to_url($blist, $pkg); |
278 |
if ($filename && -s "$cachedir/partial/$filename") { |
279 |
if (my $rpm = verify_partial_rpm_and_move($urpm, $cachedir, $filename)) { |
280 |
$sources{$id} = [ $pkg->id, $rpm ]; |
281 |
} else { |
282 |
$errors{$id} = [ $url, 'bad' ]; |
283 |
} |
284 |
} else { |
285 |
$errors{$id} = [ $url, 'missing' ]; |
286 |
} |
287 |
} |
288 |
(\%sources, \%errors); |
289 |
} |
290 |
|
291 |
1; |