1 |
package urpm::main_loop; |
2 |
|
3 |
# $Id: main_loop.pm 271299 2010-11-21 15:54:30Z peroyvind $ |
4 |
|
5 |
#- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 MandrakeSoft SA |
6 |
#- Copyright (C) 2005-2010 Mandriva SA |
7 |
#- |
8 |
#- This program is free software; you can redistribute it and/or modify |
9 |
#- it under the terms of the GNU General Public License as published by |
10 |
#- the Free Software Foundation; either version 2, or (at your option) |
11 |
#- any later version. |
12 |
#- |
13 |
#- This program is distributed in the hope that it will be useful, |
14 |
#- but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
#- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
#- GNU General Public License for more details. |
17 |
#- |
18 |
#- You should have received a copy of the GNU General Public License |
19 |
#- along with this program; if not, write to the Free Software |
20 |
#- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 |
|
22 |
use strict; |
23 |
use urpm; |
24 |
use urpm::args; |
25 |
use urpm::msg; |
26 |
use urpm::install; |
27 |
use urpm::media; |
28 |
use urpm::select; |
29 |
use urpm::orphans; |
30 |
use urpm::get_pkgs; |
31 |
use urpm::signature; |
32 |
use urpm::util qw(difference2 find intersection member partition untaint); |
33 |
|
34 |
# locking is left to callers |
35 |
sub run { |
36 |
my ($urpm, $state, $something_was_to_be_done, $ask_unselect, $_requested, $callbacks) = @_; |
37 |
|
38 |
#- global boolean options |
39 |
my ($auto_select, $no_install, $install_src, $clean, $noclean, $force, $parallel, $test, $_env) = |
40 |
($::auto_select, $::no_install, $::install_src, $::clean, $::noclean, $::force, $::parallel, $::test, $::env); |
41 |
|
42 |
urpm::get_pkgs::clean_all_cache($urpm) if $clean; |
43 |
|
44 |
my ($local_sources, $blists) = urpm::get_pkgs::selected2local_and_blists($urpm, |
45 |
$state->{selected}, |
46 |
clean_other => !$noclean && $urpm->{options}{'pre-clean'}, |
47 |
); |
48 |
if (!$local_sources && !$blists) { |
49 |
$urpm->{fatal}(3, N("unable to get source packages, aborting")); |
50 |
} |
51 |
|
52 |
my %sources = %$local_sources; |
53 |
|
54 |
urpm::removable::try_mounting_non_cdroms($urpm, $blists); |
55 |
|
56 |
$callbacks->{pre_removable} and $callbacks->{pre_removable}->(); |
57 |
require urpm::cdrom; |
58 |
urpm::cdrom::copy_packages_of_removable_media($urpm, |
59 |
$blists, \%sources, |
60 |
$callbacks->{copy_removable}); |
61 |
$callbacks->{post_removable} and $callbacks->{post_removable}->(); |
62 |
|
63 |
# use a anonymous subroutine, as it force perl to rebind external variables, like |
64 |
# $callbacks and $urpm, as showed by warnings.pm. Fix #54842 |
65 |
my $download_packages = sub { |
66 |
my ($blists, $sources) = @_; |
67 |
my @error_sources; |
68 |
urpm::get_pkgs::download_packages_of_distant_media($urpm, |
69 |
$blists, |
70 |
$sources, |
71 |
\@error_sources, |
72 |
quiet => $options{verbose} < 0, |
73 |
callback => $callbacks->{trans_log}, |
74 |
ask_retry => !$urpm->{options}{auto} && ($callbacks->{ask_retry} || sub { |
75 |
my ($raw_msg, $msg) = @_; |
76 |
if (my $download_errors = delete $urpm->{download_errors}) { |
77 |
$raw_msg = join("\n", @$download_errors, ''); |
78 |
} |
79 |
$callbacks->{ask_yes_or_no}('', $raw_msg . "\n" . $msg . "\n" . N("Retry?")); |
80 |
}), |
81 |
); |
82 |
my @msgs; |
83 |
if (@error_sources) { |
84 |
$_->[0] = urpm::download::hide_password($_->[0]) foreach @error_sources; |
85 |
my @bad = grep { $_->[1] eq 'bad' } @error_sources; |
86 |
my @missing = grep { $_->[1] eq 'missing' } @error_sources; |
87 |
|
88 |
if (@missing) { |
89 |
push @msgs, N("Installation failed, some files are missing:\n%s", |
90 |
join("\n", map { " $_->[0]" } @missing)) |
91 |
. "\n" . |
92 |
N("You may need to update your urpmi database."); |
93 |
} |
94 |
if (@bad) { |
95 |
push @msgs, N("Installation failed, bad rpms:\n%s", |
96 |
join("\n", map { " $_->[0]" } @bad)); |
97 |
} |
98 |
} |
99 |
|
100 |
(\@error_sources, \@msgs); |
101 |
}; |
102 |
|
103 |
if (exists $urpm->{options}{'download-all'}) { |
104 |
if ($urpm->{options}{'download-all'}) { |
105 |
$urpm->{cachedir} = $urpm->{'urpmi-root'} . $urpm->{options}{'download-all'}; |
106 |
urpm::init_cache_dir($urpm, $urpm->{cachedir}); |
107 |
} |
108 |
my (undef, $available) = urpm::sys::df("$urpm->{cachedir}/rpms"); |
109 |
|
110 |
if (!$urpm->{options}{ignoresize}) { |
111 |
my ($download_size) = urpm::get_pkgs::get_distant_media_filesize($urpm, $blists, \%sources); |
112 |
if ($download_size >= $available*1000) { |
113 |
my $p = N("There is not enough space on your filesystem to download all packages (%s needed, %s available).\nAre you sure you want to continue?", formatXiB($download_size), formatXiB($available*1000)); |
114 |
$force || urpm::msg::ask_yes_or_no($p) or return 10; |
115 |
} |
116 |
} |
117 |
|
118 |
#download packages one by one so that we don't try to download them again |
119 |
#and again if the user has to restart urpmi because of some failure |
120 |
my %downloaded_pkgs; |
121 |
foreach my $blist (@$blists) { |
122 |
foreach my $pkg (keys %{$blist->{pkgs}}) { |
123 |
next if $downloaded_pkgs{$pkg}; |
124 |
my $blist_one = [{ pkgs => { $pkg => $blist->{pkgs}{$pkg} }, medium => $blist->{medium} }]; |
125 |
my ($error_sources) = &$download_packages($blist_one, \%sources); |
126 |
if (@$error_sources) { |
127 |
return 10; |
128 |
} |
129 |
$downloaded_pkgs{$pkg} = 1; |
130 |
} |
131 |
} |
132 |
} |
133 |
|
134 |
#- now create transaction just before installation, this will save user impression of slowness. |
135 |
#- split of transaction should be disabled if --test is used. |
136 |
urpm::install::build_transaction_set_($urpm, $state, |
137 |
nodeps => $urpm->{options}{'allow-nodeps'} || $urpm->{options}{'allow-force'}, |
138 |
keep => $urpm->{options}{keep}, |
139 |
split_level => $urpm->{options}{'split-level'}, |
140 |
split_length => !$test && $urpm->{options}{'split-length'}); |
141 |
|
142 |
if ($options{debug__do_not_install}) { |
143 |
$urpm->{debug} = sub { print STDERR "$_[0]\n" }; |
144 |
} |
145 |
|
146 |
$urpm->{debug} and $urpm->{debug}(join("\n", "scheduled sets of transactions:", |
147 |
urpm::install::transaction_set_to_string($urpm, $state->{transaction} || []))); |
148 |
|
149 |
$options{debug__do_not_install} and exit 0; |
150 |
|
151 |
my ($ok, $nok) = (0, 0); |
152 |
my (@errors, @formatted_errors); |
153 |
my $exit_code = 0; |
154 |
|
155 |
my $migrate_back_rpmdb_db_version = |
156 |
$urpm->{root} && urpm::select::should_we_migrate_back_rpmdb_db_version($urpm, $state); |
157 |
|
158 |
foreach my $set (@{$state->{transaction} || []}) { |
159 |
|
160 |
#- put a blank line to separate with previous transaction or user question. |
161 |
$urpm->{print}("\n") if $options{verbose} >= 0; |
162 |
|
163 |
my ($transaction_blists, $transaction_sources) = |
164 |
urpm::install::prepare_transaction($urpm, $set, $blists, \%sources); |
165 |
|
166 |
#- first, filter out what is really needed to download for this small transaction. |
167 |
my ($error_sources, $msgs) = &$download_packages($transaction_blists, $transaction_sources); |
168 |
if (@$error_sources) { |
169 |
$nok++; |
170 |
my $go_on; |
171 |
if ($urpm->{options}{auto}) { |
172 |
push @formatted_errors, @$msgs; |
173 |
} else { |
174 |
my $sub = $callbacks->{ask_for_bad_or_missing} || $callbacks->{ask_yes_or_no}; |
175 |
$go_on = $sub->( |
176 |
N("Installation failed"), |
177 |
join("\n\n", @$msgs, N("Try to continue anyway?"))); |
178 |
} |
179 |
if (!$go_on) { |
180 |
my @missing = grep { $_->[1] eq 'missing' } @$error_sources; |
181 |
if (@missing) { |
182 |
$exit_code = $ok ? 13 : 14; |
183 |
} |
184 |
last; |
185 |
} |
186 |
} |
187 |
|
188 |
$callbacks->{post_download} and $callbacks->{post_download}->(); |
189 |
my %transaction_sources_install = %{$urpm->extract_packages_to_install($transaction_sources, $state) || {}}; |
190 |
$callbacks->{post_extract} and $callbacks->{post_extract}->($set, $transaction_sources, \%transaction_sources_install); |
191 |
|
192 |
if (!$force && ($urpm->{options}{'verify-rpm'} || grep { $_->{'verify-rpm'} } @{$urpm->{media}})) { |
193 |
$callbacks->{pre_check_sig} and $callbacks->{pre_check_sig}->(); |
194 |
# CHECK ME: rpmdrake passed "basename => 1" option: |
195 |
my @bad_signatures = urpm::signature::check($urpm, \%transaction_sources_install, $transaction_sources, |
196 |
callback => $callbacks->{check_sig} |
197 |
); |
198 |
|
199 |
if (@bad_signatures) { |
200 |
my $msg = @bad_signatures == 1 ? |
201 |
N("The following package has bad signature") |
202 |
: N("The following packages have bad signatures"); |
203 |
my $msg2 = N("Do you want to continue installation ?"); |
204 |
my $p = join "\n", @bad_signatures; |
205 |
$callbacks->{bad_signature}->("$msg:\n$p\n", $msg2) or return 16; |
206 |
} |
207 |
} |
208 |
|
209 |
#- install source package only (whatever the user is root or not, but use rpm for that). |
210 |
if ($install_src) { |
211 |
if (my @l = grep { /\.src\.rpm$/ } values %transaction_sources_install, values %$transaction_sources) { |
212 |
my $rpm_opt = $options{verbose} >= 0 ? 'vh' : ''; |
213 |
system("rpm", "-i$rpm_opt", @l, ($urpm->{root} ? ("--root", $urpm->{root}) : @{[]})); |
214 |
#- Warning : the following message is parsed in urpm::parallel_* |
215 |
if ($?) { |
216 |
$urpm->{print}(N("Installation failed")); |
217 |
++$nok; |
218 |
} elsif ($urpm->{options}{'post-clean'}) { |
219 |
if (my @tmp_srpm = grep { urpm::is_temporary_file($urpm, $_) } @l) { |
220 |
$urpm->{log}(N("removing installed rpms (%s)", join(' ', @tmp_srpm))); |
221 |
unlink @tmp_srpm; |
222 |
} |
223 |
} |
224 |
} |
225 |
next; |
226 |
} |
227 |
|
228 |
next if $no_install; |
229 |
|
230 |
#- clean to remove any src package now. |
231 |
foreach (\%transaction_sources_install, $transaction_sources) { |
232 |
foreach my $id (keys %$_) { |
233 |
my $pkg = $urpm->{depslist}[$id] or next; |
234 |
$pkg->arch eq 'src' and delete $_->{$id}; |
235 |
} |
236 |
} |
237 |
|
238 |
if (keys(%transaction_sources_install) || keys(%$transaction_sources) || $set->{remove}) { |
239 |
if ($parallel) { |
240 |
$urpm->{print}(N("distributing %s", join(' ', values %transaction_sources_install, values %$transaction_sources))); |
241 |
#- no remove are handle here, automatically done by each distant node. |
242 |
$urpm->{log}("starting distributed install"); |
243 |
$urpm->{parallel_handler}->parallel_install( |
244 |
$urpm, |
245 |
[ keys %{$state->{rejected} || {}} ], \%transaction_sources_install, $transaction_sources, |
246 |
test => $test, |
247 |
excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}, |
248 |
); |
249 |
} else { |
250 |
if ($options{verbose} >= 0) { |
251 |
if (my @packnames = (values %transaction_sources_install, values %$transaction_sources)) { |
252 |
(my $common_prefix) = $packnames[0] =~ m!^(.*)/!; |
253 |
if (length($common_prefix) && @packnames == grep { m!^\Q$common_prefix/! } @packnames) { |
254 |
#- there's a common prefix, simplify message |
255 |
$urpm->{print}(N("installing %s from %s", join(' ', map { s!.*/!!; $_ } @packnames), $common_prefix)); |
256 |
} else { |
257 |
$urpm->{print}(N("installing %s", join "\n", @packnames)); |
258 |
} |
259 |
} |
260 |
} |
261 |
my $to_remove = $urpm->{options}{'allow-force'} ? [] : $set->{remove} || []; |
262 |
bug_log(scalar localtime(), " ", join(' ', values %transaction_sources_install, values %$transaction_sources), "\n"); |
263 |
$urpm->{log}("starting installing packages"); |
264 |
my %install_options_common = ( |
265 |
urpm::install::options($urpm), |
266 |
test => $test, |
267 |
verbose => $options{verbose}, |
268 |
script_fd => $urpm->{options}{script_fd}, |
269 |
oldpackage => $state->{oldpackage}, |
270 |
justdb => $options{justdb}, |
271 |
replacepkgs => $options{replacepkgs}, |
272 |
callback_close_helper => $callbacks->{close_helper}, |
273 |
callback_inst => $callbacks->{inst}, |
274 |
callback_open_helper => $callbacks->{open_helper}, |
275 |
callback_trans => $callbacks->{trans}, |
276 |
callback_report_uninst => $callbacks->{callback_report_uninst}, |
277 |
raw_message => 1, |
278 |
); |
279 |
|
280 |
urpm::orphans::add_unrequested($urpm, $state) if !$test; |
281 |
|
282 |
install: |
283 |
my @l = urpm::install::install($urpm, |
284 |
$to_remove, |
285 |
\%transaction_sources_install, $transaction_sources, |
286 |
%install_options_common, |
287 |
); |
288 |
if (@l) { |
289 |
my ($raw_error, $translated) = partition { /^(badarch|bados|installed|badrelocate|conflicts|installed|diskspace|disknodes|requires|conflicts|unknown)\@/ } @l; |
290 |
@l = @$translated; |
291 |
my $fatal = find { /^disk/ } @$raw_error; |
292 |
my $no_question = $fatal || $urpm->{options}{auto}; |
293 |
|
294 |
#- Warning : the following message is parsed in urpm::parallel_* |
295 |
my $msg = N("Installation failed:") . "\n" . join("\n", map { "\t$_" } @l) . "\n"; |
296 |
if (!$no_question && !$install_options_common{nodeps} && ($urpm->{options}{'allow-nodeps'} || $urpm->{options}{'allow-force'})) { |
297 |
if ($callbacks->{ask_yes_or_no}->(N("Installation failed"), |
298 |
$msg . N("Try installation without checking dependencies?"))) { |
299 |
$urpm->{log}("starting installing packages without deps"); |
300 |
$install_options_common{nodeps} = 1; |
301 |
goto install; |
302 |
} |
303 |
} elsif (!$no_question && !$install_options_common{force} && $urpm->{options}{'allow-force'}) { |
304 |
if ($callbacks->{ask_yes_or_no}->(N("Installation failed"), |
305 |
$msg . N("Try harder to install (--force)?"))) { |
306 |
$urpm->{log}("starting force installing packages without deps"); |
307 |
$install_options_common{force} = 1; |
308 |
goto install; |
309 |
} |
310 |
} |
311 |
$urpm->{log}($msg); |
312 |
|
313 |
++$nok; |
314 |
push @errors, @l; |
315 |
$fatal and last; |
316 |
} else { |
317 |
++$ok; |
318 |
} |
319 |
} |
320 |
} |
321 |
if ($callbacks->{is_canceled}) { |
322 |
last if $callbacks->{is_canceled}->(); |
323 |
} |
324 |
} |
325 |
|
326 |
if ($migrate_back_rpmdb_db_version) { |
327 |
urpm::sys::migrate_back_rpmdb_db_version($urpm, $urpm->{root}); |
328 |
} |
329 |
|
330 |
$callbacks->{completed} and $callbacks->{completed}->(); |
331 |
|
332 |
if ($nok) { |
333 |
$callbacks->{trans_error_summary} and $callbacks->{trans_error_summary}->($nok, \@errors); |
334 |
if (@formatted_errors) { |
335 |
$urpm->{print}(join("\n", @formatted_errors)); |
336 |
} |
337 |
if (@errors) { |
338 |
$urpm->{print}(N("Installation failed:") . join("\n", map { "\t$_" } @errors)); |
339 |
} |
340 |
$exit_code ||= $ok ? 11 : 12; |
341 |
} else { |
342 |
$callbacks->{success_summary} and $callbacks->{success_summary}->(); |
343 |
if ($something_was_to_be_done || $auto_select) { |
344 |
if (@{$state->{transaction} || []} == 0 && @$ask_unselect == 0) { |
345 |
if ($auto_select) { |
346 |
if ($options{verbose} >= 0) { |
347 |
#- Warning : the following message is parsed in urpm::parallel_* |
348 |
$urpm->{print}(N("Packages are up to date")); |
349 |
} |
350 |
} else { |
351 |
if ($callbacks->{already_installed_or_not_installable}) { |
352 |
my $msg = urpm::select::translate_already_installed($state); |
353 |
$callbacks->{already_installed_or_not_installable}->([$msg], []); |
354 |
} |
355 |
} |
356 |
$exit_code = 15 if our $expect_install; |
357 |
} elsif ($test && $exit_code == 0) { |
358 |
#- Warning : the following message is parsed in urpm::parallel_* |
359 |
print N("Installation is possible"), "\n"; |
360 |
} else { |
361 |
handle_need_restart($urpm, $state, $callbacks); |
362 |
} |
363 |
} |
364 |
} |
365 |
$exit_code; |
366 |
} |
367 |
|
368 |
sub handle_need_restart { |
369 |
my ($urpm, $state, $callbacks) = @_; |
370 |
|
371 |
return if $urpm->{root} && !$ENV{URPMI_TEST_RESTART}; |
372 |
return if !$callbacks->{need_restart}; |
373 |
|
374 |
if (intersection([ keys %{$state->{selected}} ], |
375 |
[ keys %{$urpm->{provides}{'should-restart'}} ])) { |
376 |
if (my $need_restart_formatted = urpm::sys::need_restart_formatted($urpm->{root})) { |
377 |
$callbacks->{need_restart}($need_restart_formatted); |
378 |
|
379 |
# need_restart() accesses rpm db, so we need to ensure things are clean: |
380 |
urpm::sys::may_clean_rpmdb_shared_regions($urpm, $options{test}); |
381 |
} |
382 |
} |
383 |
} |
384 |
|
385 |
1; |