1 |
package Rpmdrake::pkg; |
2 |
#***************************************************************************** |
3 |
# |
4 |
# Copyright (c) 2002 Guillaume Cottenceau |
5 |
# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> |
6 |
# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA |
7 |
# Copyright (c) 2005-2007 Mandriva SA |
8 |
# |
9 |
# This program is free software; you can redistribute it and/or modify |
10 |
# it under the terms of the GNU General Public License version 2, as |
11 |
# published by the Free Software Foundation. |
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 |
#***************************************************************************** |
23 |
# |
24 |
# $Id: pkg.pm 270160 2010-06-22 19:55:40Z jvictor $ |
25 |
|
26 |
use strict; |
27 |
use MDK::Common::Func 'any'; |
28 |
use lib qw(/usr/lib/libDrakX); |
29 |
use common; |
30 |
use POSIX qw(_exit); |
31 |
use URPM; |
32 |
use utf8; |
33 |
use Rpmdrake::open_db; |
34 |
use Rpmdrake::gurpm; |
35 |
use Rpmdrake::formatting; |
36 |
use Rpmdrake::rpmnew; |
37 |
|
38 |
use rpmdrake; |
39 |
use urpm; |
40 |
use urpm::lock; |
41 |
use urpm::install; |
42 |
use urpm::signature; |
43 |
use urpm::get_pkgs; |
44 |
use urpm::select; |
45 |
use urpm::main_loop; |
46 |
use urpm::args qw(); |
47 |
|
48 |
|
49 |
use Exporter; |
50 |
our @ISA = qw(Exporter); |
51 |
our @EXPORT = qw( |
52 |
$priority_up_alread_warned |
53 |
download_callback |
54 |
extract_header |
55 |
find_installed_version |
56 |
get_pkgs |
57 |
perform_installation |
58 |
perform_removal |
59 |
run_rpm); |
60 |
|
61 |
use mygtk2 qw(gtknew); |
62 |
use ugtk2 qw(:all); |
63 |
|
64 |
our $priority_up_alread_warned; |
65 |
|
66 |
|
67 |
sub run_rpm { |
68 |
foreach (qw(LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)) { |
69 |
local $ENV{$_} = $ENV{$_} . '.UTF-8' if $ENV{$_} && $ENV{$_} !~ /UTF-8/; |
70 |
} |
71 |
my @l = map { ensure_utf8($_) } run_program::get_stdout(@_); |
72 |
wantarray() ? @l : join('', @l); |
73 |
} |
74 |
|
75 |
|
76 |
sub extract_header { |
77 |
my ($pkg, $urpm, $xml_info, $o_installed_version) = @_; |
78 |
my %fields = ( |
79 |
info => 'description', |
80 |
files => 'files', |
81 |
changelog => 'changelog', |
82 |
); |
83 |
# already extracted: |
84 |
return if $pkg->{$fields{$xml_info}}; |
85 |
|
86 |
my $p = $pkg->{pkg}; |
87 |
|
88 |
if (!$p) { |
89 |
warn ">> ghost package '$pkg' has no URPM object!!!\n"; |
90 |
return; |
91 |
} |
92 |
|
93 |
my $name = urpm_name($p); |
94 |
# fix extracting info for SRPMS and RPM GPG keys: |
95 |
$name =~ s!\.src!!; |
96 |
|
97 |
if ($p->flag_installed && !$p->flag_upgrade) { |
98 |
my @files = map { chomp_($_) } run_rpm("rpm -ql $name"); |
99 |
add2hash($pkg, { files => [ @files ? @files : N("(none)") ], |
100 |
description => rpm_description(scalar(run_rpm("rpm -q --qf '%{description}' $name"))), |
101 |
changelog => format_changelog_string($o_installed_version, scalar(run_rpm("rpm -q --changelog $name"))) }); |
102 |
} else { |
103 |
my $medium = pkg2medium($p, $urpm); |
104 |
my ($local_source, %xml_info_pkgs, $bar_id); |
105 |
my $_statusbar_clean_guard = before_leaving { $bar_id and statusbar_msg_remove($bar_id) }; |
106 |
my $dir = urpm::file_from_local_url($medium->{url}); |
107 |
if ($dir) { |
108 |
$local_source = "$dir/" . $p->filename; |
109 |
} |
110 |
if (-e $local_source) { |
111 |
$bar_id = statusbar_msg(N("Getting information from XML meta-data from %s...", $dir), 0); |
112 |
$urpm->{log}("getting information from rpms from $dir"); |
113 |
} else { |
114 |
my $gurpm; |
115 |
$bar_id = statusbar_msg(N("Getting '%s' from XML meta-data...", $xml_info), 0); |
116 |
my $_gurpm_clean_guard = before_leaving { undef $gurpm }; |
117 |
if (my $xml_info_file = eval { urpm::media::any_xml_info($urpm, $medium, $xml_info, undef, sub { |
118 |
$gurpm ||= Rpmdrake::gurpm->new(N("Please wait"), |
119 |
'', # FIXME: add a real string after cooker |
120 |
transient => $::main_window); |
121 |
download_callback($gurpm, @_) |
122 |
or goto header_non_available; |
123 |
}) }) { |
124 |
require urpm::xml_info; |
125 |
require urpm::xml_info_pkg; |
126 |
$urpm->{log}("getting information from $xml_info_file"); |
127 |
my %nodes = eval { urpm::xml_info::get_nodes($xml_info, $xml_info_file, [ $name ]) }; |
128 |
goto header_non_available if $@; |
129 |
put_in_hash($xml_info_pkgs{$name} ||= {}, $nodes{$name}); |
130 |
} else { |
131 |
if ($xml_info eq 'info') { |
132 |
$urpm->{info}(N("No xml info for medium \"%s\", only partial result for package %s", $medium->{name}, $name)); |
133 |
} else { |
134 |
$urpm->{error}(N("No xml info for medium \"%s\", unable to return any result for package %s", $medium->{name}, $name)); |
135 |
} |
136 |
} |
137 |
} |
138 |
|
139 |
#- even if non-root, search for a header in the global cachedir |
140 |
my $file = $local_source; |
141 |
if (-s $file) { |
142 |
$p->update_header($file) or do { |
143 |
warn "Warning, could not extract header for $name from $medium!"; |
144 |
goto header_non_available; |
145 |
}; |
146 |
add2hash($pkg, { description => rpm_description($p->description), |
147 |
files => scalar($p->files) ? [ $p->files ] : [ N("(none)") ], |
148 |
url => $p->url, |
149 |
changelog => format_changelog_changelogs($o_installed_version, $p->changelogs) }); |
150 |
$p->pack_header; # needed in order to call methods on objects outside ->traverse |
151 |
} elsif ($xml_info_pkgs{$name}) { |
152 |
if ($xml_info eq 'info') { |
153 |
add2hash($pkg, { description => rpm_description($xml_info_pkgs{$name}{description}), |
154 |
url => $xml_info_pkgs{$name}{url} |
155 |
}); |
156 |
} elsif ($xml_info eq 'files') { |
157 |
my @files = map { chomp_(to_utf8($_)) } split("\n", $xml_info_pkgs{$name}{files}); |
158 |
add2hash($pkg, { files => [ @files ? @files : N("(none)") ] }); |
159 |
} elsif ($xml_info eq 'changelog') { |
160 |
add2hash($pkg, { |
161 |
changelog => format_changelog_changelogs($o_installed_version, |
162 |
@{$xml_info_pkgs{$name}{changelogs}}) |
163 |
}); |
164 |
} |
165 |
$p->pack_header; # needed in order to call methods on objects outside ->traverse |
166 |
} else { |
167 |
goto header_non_available; |
168 |
} |
169 |
return; |
170 |
header_non_available: |
171 |
add2hash($pkg, { summary => $p->summary || N("(Not available)"), description => undef }); |
172 |
} |
173 |
} |
174 |
|
175 |
sub find_installed_version { |
176 |
my ($p) = @_; |
177 |
my @version; |
178 |
open_rpm_db()->traverse_tag('name', [ $p->name ], sub { push @version, $_[0]->version . '-' . $_[0]->release }); |
179 |
@version ? join(',', sort @version) : N("(none)"); |
180 |
} |
181 |
|
182 |
my $canceled; |
183 |
sub download_callback { |
184 |
my ($gurpm, $mode, $file, $percent, $total, $eta, $speed) = @_; |
185 |
$canceled = 0; |
186 |
if ($mode eq 'start') { |
187 |
$gurpm->label(N("Downloading package `%s'...", basename($file))); |
188 |
$gurpm->validate_cancel(but(N("Cancel")), sub { $canceled = 1 }); |
189 |
} elsif ($mode eq 'progress') { |
190 |
$gurpm->label( |
191 |
join("\n", |
192 |
N("Downloading package `%s'...", basename($file)), |
193 |
(defined $total && defined $eta ? |
194 |
N(" %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed) |
195 |
: N(" %s%% completed, speed = %s", $percent, $speed) |
196 |
) =~ /^\s*(.*)/ |
197 |
), |
198 |
); |
199 |
$gurpm->progress($percent/100); |
200 |
} elsif ($mode eq 'end') { |
201 |
$gurpm->progress(1); |
202 |
$gurpm->invalidate_cancel; |
203 |
} |
204 |
!$canceled; |
205 |
} |
206 |
|
207 |
|
208 |
# -=-=-=---=-=-=---=-=-=-- install packages -=-=-=---=-=-=---=-=-=- |
209 |
|
210 |
my (@update_medias, $is_update_media_already_asked); |
211 |
|
212 |
sub warn_about_media { |
213 |
my ($w, $opts) = @_; |
214 |
|
215 |
return if $::MODE ne 'update'; |
216 |
return if $::rpmdrake_options{'no-media-update'}; |
217 |
|
218 |
# we use our own instance of the urpmi db in order not to mess up with skip-list managment (#31092): |
219 |
# and no need to fully configure urpmi since we may have to do it again anyway because of new media: |
220 |
my $urpm = fast_open_urpmi_db(); |
221 |
|
222 |
my $_lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}); |
223 |
|
224 |
# build media list: |
225 |
@update_medias = get_update_medias($urpm); |
226 |
|
227 |
# do not update again media after installing/removing some packages: |
228 |
$::rpmdrake_options{'no-media-update'} ||= 1; |
229 |
|
230 |
if (@update_medias > 0) { |
231 |
if (!$opts->{skip_updating_mu} && !$is_update_media_already_asked) { |
232 |
$is_update_media_already_asked = 1; |
233 |
$::rpmdrake_options{'no-confirmation'} or interactive_msg(N("Confirmation"), |
234 |
N("I need to contact the mirror to get latest update packages. |
235 |
Please check that your network is currently running. |
236 |
|
237 |
Is it ok to continue?"), yesno => 1, |
238 |
widget => gtknew('CheckButton', text => N("Do not ask me next time"), |
239 |
active_ref => \$::rpmdrake_options{'no-confirmation'} |
240 |
)) or myexit(-1); |
241 |
writeconf(); |
242 |
urpm::media::select_media($urpm, map { $_->{name} } @update_medias); |
243 |
update_sources($urpm, noclean => 1, medialist => [ map { $_->{name} } @update_medias ]); |
244 |
} |
245 |
} else { |
246 |
if (any { $_->{update} } @{$urpm->{media}}) { |
247 |
interactive_msg(N("Already existing update media"), |
248 |
N("You already have at least one update medium configured, but |
249 |
all of them are currently disabled. You should run the Software |
250 |
Media Manager to enable at least one (check it in the \"%s\" |
251 |
column). |
252 |
|
253 |
Then, restart \"%s\".", N("Enabled"), $rpmdrake::myname_update)); |
254 |
myexit(-1); |
255 |
} |
256 |
my ($mirror) = choose_mirror($urpm, transient => $w->{real_window} || $::main_window, |
257 |
message => join("\n\n", |
258 |
N("You have no configured update media. MageiaUpdate cannot operate without any update media."), |
259 |
N("I need to contact the Mageia website to get the mirror list. |
260 |
Please check that your network is currently running. |
261 |
|
262 |
Is it ok to continue?"), |
263 |
), |
264 |
); |
265 |
my $m = ref($mirror) ? $mirror->{url} : ''; |
266 |
$m or interactive_msg(N("How to choose manually your mirror"), |
267 |
N("You may also choose your desired mirror manually: to do so, |
268 |
launch the Software Media Manager, and then add a `Security |
269 |
updates' medium. |
270 |
|
271 |
Then, restart %s.", $rpmdrake::myname_update)), myexit(-1); |
272 |
add_distrib_update_media($urpm, $mirror, only_updates => 1); |
273 |
} |
274 |
} |
275 |
|
276 |
|
277 |
sub get_parallel_group() { |
278 |
$::rpmdrake_options{parallel} ? $::rpmdrake_options{parallel}[0] : undef; |
279 |
} |
280 |
|
281 |
my ($count, $level, $limit, $new_stage, $prev_stage, $total); |
282 |
|
283 |
sub init_progress_bar { |
284 |
my ($urpm) = @_; |
285 |
undef $_ foreach $count, $prev_stage, $new_stage, $limit; |
286 |
$level = 0.05; |
287 |
$total = @{$urpm->{depslist}}; |
288 |
} |
289 |
|
290 |
sub reset_pbar_count { |
291 |
undef $prev_stage; |
292 |
$count = 0; |
293 |
$limit = $_[0]; |
294 |
} |
295 |
|
296 |
sub update_pbar { |
297 |
my ($gurpm) = @_; |
298 |
return if !$total; # don't die if there's no source |
299 |
$count++; |
300 |
$new_stage = $level+($limit-$level)*$count/$total; |
301 |
if ($prev_stage + 0.01 < $new_stage) { |
302 |
$prev_stage = $new_stage; |
303 |
$gurpm->progress($new_stage); |
304 |
} |
305 |
} |
306 |
|
307 |
|
308 |
sub get_installed_packages { |
309 |
my ($urpm, $db, $all_pkgs, $gurpm) = @_; |
310 |
|
311 |
my @base = ("basesystem", split /,\s*/, $urpm->{global_config}{'prohibit-remove'}); |
312 |
my (%base, %basepackages, @installed_pkgs, @processed_base); |
313 |
reset_pbar_count(0.33); |
314 |
while (defined(local $_ = shift @base)) { |
315 |
exists $basepackages{$_} and next; |
316 |
$db->traverse_tag(m|^/| ? 'path' : 'whatprovides', [ $_ ], sub { |
317 |
update_pbar($gurpm); |
318 |
my $name = urpm_name($_[0]); |
319 |
# workaround looping in URPM: |
320 |
return if member($name, @processed_base); |
321 |
push @processed_base, $name; |
322 |
push @{$basepackages{$_}}, $name; |
323 |
push @base, $_[0]->requires_nosense; |
324 |
}); |
325 |
} |
326 |
foreach (values %basepackages) { |
327 |
my $n = @$_; #- count number of times it's provided |
328 |
foreach (@$_) { |
329 |
$base{$_} = \$n; |
330 |
} |
331 |
} |
332 |
# costly: |
333 |
$db->traverse(sub { |
334 |
my ($pkg) = @_; |
335 |
update_pbar($gurpm); |
336 |
my $fullname = urpm_name($pkg); |
337 |
return if $fullname =~ /@/; |
338 |
$all_pkgs->{$fullname} = { |
339 |
selected => 0, pkg => $pkg, urpm_name => urpm_name($pkg), |
340 |
} if !($all_pkgs->{$fullname} && $all_pkgs->{$fullname}{description}); |
341 |
if (my $name = $base{$fullname}) { |
342 |
$all_pkgs->{$fullname}{base} = \$name; |
343 |
$pkg->set_flag_base(1) if $$name == 1; |
344 |
} |
345 |
push @installed_pkgs, $fullname; |
346 |
$pkg->set_flag_installed; |
347 |
$pkg->pack_header; # needed in order to call methods on objects outside ->traverse |
348 |
}); |
349 |
@installed_pkgs; |
350 |
} |
351 |
|
352 |
urpm::select::add_packages_to_priority_upgrade_list('rpmdrake'); |
353 |
|
354 |
my ($priority_state, $priority_requested); |
355 |
our $need_restart; |
356 |
|
357 |
our $probe_only_for_updates; |
358 |
|
359 |
sub get_updates_list { |
360 |
my ($urpm, $db, $state, $requested, $requested_list, $requested_strict, $all_pkgs) = @_; |
361 |
|
362 |
$urpm->request_packages_to_upgrade( |
363 |
$db, |
364 |
$state, |
365 |
$requested, |
366 |
); |
367 |
|
368 |
my %common_opts = ( |
369 |
callback_choices => \&Rpmdrake::gui::callback_choices, |
370 |
priority_upgrade => $urpm->{options}{'priority-upgrade'}, |
371 |
); |
372 |
|
373 |
if ($urpm->{options}{'priority-upgrade'}) { |
374 |
$need_restart = |
375 |
urpm::select::resolve_priority_upgrades_after_auto_select($urpm, $db, $state, |
376 |
$requested, %common_opts); |
377 |
} |
378 |
|
379 |
# list of updates (including those matching /etc/urpmi/skip.list): |
380 |
@$requested_list = sort map { |
381 |
my $name = urpm_name($_); |
382 |
$all_pkgs->{$name} = { pkg => $_ }; |
383 |
$name; |
384 |
} @{$urpm->{depslist}}[keys %$requested]; |
385 |
|
386 |
# list of pure updates (w/o those matching /etc/urpmi/skip.list but with their deps): |
387 |
if ($probe_only_for_updates && !$need_restart) { |
388 |
@$requested_strict = sort map { |
389 |
urpm_name($_); |
390 |
} $urpm->resolve_requested($db, $state, $requested, callback_choices => \&Rpmdrake::gui::callback_choices); |
391 |
|
392 |
if (my @l = grep { $state->{selected}{$_->id} } |
393 |
urpm::select::_priority_upgrade_pkgs($urpm, $urpm->{options}{'priority-upgrade'})) { |
394 |
if (!$need_restart) { |
395 |
$need_restart = |
396 |
urpm::select::_resolve_priority_upgrades($urpm, $db, $state, $state->{selected}, |
397 |
\@l, %common_opts); |
398 |
} |
399 |
} |
400 |
} |
401 |
|
402 |
if ($need_restart) { |
403 |
$requested_strict = [ map { scalar $_->fullname } @{$urpm->{depslist}}[keys %{$state->{selected}}] ]; |
404 |
# drop non priority updates: |
405 |
@$requested_list = (); |
406 |
} |
407 |
|
408 |
# list updates including skiped ones + their deps in MageiaUpdate: |
409 |
@$requested_list = uniq(@$requested_list, @$requested_strict); |
410 |
|
411 |
# do not pre select updates in rpmdrake: |
412 |
@$requested_strict = () if !$probe_only_for_updates; |
413 |
} |
414 |
|
415 |
sub get_pkgs { |
416 |
my ($opts) = @_; |
417 |
my $w = $::main_window; |
418 |
|
419 |
my $gurpm = Rpmdrake::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); |
420 |
my $_gurpm_clean_guard = before_leaving { undef $gurpm }; |
421 |
#my $_flush_guard = Gtk2::GUI_Update_Guard->new; |
422 |
|
423 |
warn_about_media($w, $opts); |
424 |
|
425 |
my $urpm = open_urpmi_db(update => $probe_only_for_updates && !is_it_a_devel_distro()); |
426 |
|
427 |
my $_drop_lock = before_leaving { undef $urpm->{lock} }; |
428 |
|
429 |
$priority_up_alread_warned = 0; |
430 |
|
431 |
# update media list in case warn_about_media() added some: |
432 |
@update_medias = get_update_medias($urpm); |
433 |
|
434 |
$gurpm->label(N("Reading updates description")); |
435 |
$gurpm->progress(0.05); |
436 |
|
437 |
#- parse the description file |
438 |
my $update_descr = urpm::get_updates_description($urpm, @update_medias); |
439 |
|
440 |
my $_unused = N("Please wait, finding available packages..."); |
441 |
|
442 |
# find out installed packages: |
443 |
|
444 |
init_progress_bar($urpm); |
445 |
|
446 |
$gurpm->label(N("Please wait, listing base packages...")); |
447 |
$gurpm->progress($level); |
448 |
|
449 |
my $db = eval { open_rpm_db() }; |
450 |
if (my $err = $@) { |
451 |
interactive_msg(N("Error"), N("A fatal error occurred: %s.", $err)); |
452 |
return; |
453 |
} |
454 |
|
455 |
my $sig_handler = sub { undef $db; exit 3 }; |
456 |
local $SIG{INT} = $sig_handler; |
457 |
local $SIG{QUIT} = $sig_handler; |
458 |
|
459 |
$gurpm->label(N("Please wait, finding installed packages...")); |
460 |
$gurpm->progress($level = 0.33); |
461 |
reset_pbar_count(0.66); |
462 |
my (@installed_pkgs, %all_pkgs); |
463 |
if (!$probe_only_for_updates) { |
464 |
@installed_pkgs = get_installed_packages($urpm, $db, \%all_pkgs, $gurpm); |
465 |
} |
466 |
|
467 |
if (my $group = get_parallel_group()) { |
468 |
urpm::media::configure($urpm, parallel => $group); |
469 |
} |
470 |
|
471 |
# find out availlable packages: |
472 |
|
473 |
$urpm->{state} = {}; |
474 |
my (@installable_pkgs, @updates); |
475 |
|
476 |
$gurpm->label(N("Please wait, finding available packages...")); |
477 |
$gurpm->progress($level = 0.66); |
478 |
|
479 |
check_update_media_version($urpm, @update_medias); |
480 |
|
481 |
my $requested = {}; |
482 |
my $state = {}; |
483 |
my (@requested, @requested_strict); |
484 |
|
485 |
if ($compute_updates->[0] || $::MODE eq 'update') { |
486 |
get_updates_list($urpm, $db, $state, $requested, \@requested, \@requested_strict, \%all_pkgs); |
487 |
} |
488 |
|
489 |
$priority_state = $need_restart ? $state : undef; |
490 |
$priority_requested = $need_restart ? $requested : undef; |
491 |
|
492 |
if (!$probe_only_for_updates) { |
493 |
$urpm->compute_installed_flags($db); # TODO/FIXME: not for updates |
494 |
$urpm->{depslist}[$_]->set_flag_installed foreach keys %$requested; #- pretend it's installed |
495 |
} |
496 |
$urpm->{rpmdrake_state} = $state; #- Don't forget it |
497 |
$gurpm->progress($level = 0.7); |
498 |
|
499 |
reset_pbar_count(1); |
500 |
foreach my $pkg (@{$urpm->{depslist}}) { |
501 |
update_pbar($gurpm); |
502 |
$pkg->flag_upgrade or next; |
503 |
my $name = urpm_name($pkg); |
504 |
push @installable_pkgs, $name; |
505 |
$all_pkgs{$name} = { pkg => $pkg }; |
506 |
} |
507 |
|
508 |
my @inactive_backports; |
509 |
my @active_backports; |
510 |
my @backport_medias = get_backport_media($urpm); |
511 |
|
512 |
foreach my $medium (@backport_medias) { |
513 |
update_pbar($gurpm); |
514 |
|
515 |
# The 'searchmedia' flag differentiates inactive backport medias |
516 |
# (because that option was passed to urpm::media::configure to |
517 |
# temporarily enable them) |
518 |
|
519 |
my $backports = |
520 |
$medium->{searchmedia} ? \@inactive_backports : \@active_backports; |
521 |
|
522 |
foreach my $pkg_id ($medium->{start} .. $medium->{end}) { |
523 |
next if !$pkg_id; |
524 |
my $pkg = $urpm->{depslist}[$pkg_id]; |
525 |
$pkg->flag_upgrade or next; |
526 |
my $name = urpm_name($pkg); |
527 |
push @$backports, $name; |
528 |
$all_pkgs{$name} = { pkg => $pkg }; |
529 |
} |
530 |
} |
531 |
@updates = @requested; |
532 |
# selecting updates by default but skipped ones (MageiaUpdate only): |
533 |
foreach (@requested_strict) { |
534 |
$all_pkgs{$_}{selected} = 1; |
535 |
} |
536 |
|
537 |
# urpmi only care about the first medium where it found the package, |
538 |
# so there's no need to list the same package several time: |
539 |
@installable_pkgs = uniq(difference2(\@installable_pkgs, \@updates)); |
540 |
|
541 |
my @meta_pkgs = grep { /^task-|^basesystem/ } keys %all_pkgs; |
542 |
|
543 |
my @gui_pkgs = map { chomp; $_ } cat_('/usr/share/rpmdrake/gui.lst'); |
544 |
# add meta packages to GUI packages list (which expect basic names not fullnames): |
545 |
push @gui_pkgs, map { (split_fullname($_))[0] } @meta_pkgs; |
546 |
|
547 |
+{ urpm => $urpm, |
548 |
all_pkgs => \%all_pkgs, |
549 |
installed => \@installed_pkgs, |
550 |
installable => \@installable_pkgs, |
551 |
updates => \@updates, |
552 |
meta_pkgs => \@meta_pkgs, |
553 |
gui_pkgs => [ grep { member(($all_pkgs{$_}{pkg}->fullname)[0], @gui_pkgs) } keys %all_pkgs ], |
554 |
update_descr => $update_descr, |
555 |
backports => [ @inactive_backports, @active_backports ], |
556 |
inactive_backports => \@inactive_backports |
557 |
}; |
558 |
} |
559 |
|
560 |
sub display_READMEs_if_needed { |
561 |
my ($urpm, $w) = @_; |
562 |
return if !$urpm->{readmes}; |
563 |
my %Readmes = %{$urpm->{readmes}}; |
564 |
if (keys %Readmes) { #- display the README*.urpmi files |
565 |
interactive_packtable( |
566 |
N("Upgrade information"), |
567 |
$w, |
568 |
N("These packages come with upgrade information"), |
569 |
[ map { |
570 |
my $fullname = $_; |
571 |
[ gtkpack__( |
572 |
gtknew('HBox'), |
573 |
gtkset_selectable(gtknew('Label', text => $Readmes{$fullname}),1), |
574 |
), |
575 |
gtksignal_connect( |
576 |
gtknew('Button', text => N("Upgrade information about this package")), |
577 |
clicked => sub { |
578 |
interactive_msg( |
579 |
N("Upgrade information about package %s", $Readmes{$fullname}), |
580 |
(join '' => formatAlaTeX(scalar cat_($fullname))), |
581 |
scroll => 1, |
582 |
); |
583 |
}, |
584 |
), |
585 |
] } keys %Readmes ], |
586 |
[ gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) ] |
587 |
); |
588 |
} |
589 |
} |
590 |
|
591 |
sub perform_parallel_install { |
592 |
my ($urpm, $group, $w, $statusbar_msg_id) = @_; |
593 |
my @pkgs = map { if_($_->flag_requested, urpm_name($_)) } @{$urpm->{depslist}}; |
594 |
|
595 |
my @error_msgs; |
596 |
my $res = !run_program::run('urpmi', '2>', \@error_msgs, '-v', '--X', '--parallel', $group, @pkgs); |
597 |
|
598 |
if ($res) { |
599 |
$$statusbar_msg_id = statusbar_msg( |
600 |
#N("Everything installed successfully"), |
601 |
N("All requested packages were installed successfully."), |
602 |
); |
603 |
} else { |
604 |
interactive_msg( |
605 |
N("Problem during installation"), |
606 |
N("There was a problem during the installation:\n\n%s", join("\n", @error_msgs)), |
607 |
scroll => 1, |
608 |
); |
609 |
} |
610 |
open_rpm_db('force_sync'); |
611 |
$w->set_sensitive(1); |
612 |
return 0; |
613 |
} |
614 |
|
615 |
sub perform_installation { #- (partially) duplicated from /usr/sbin/urpmi :-( |
616 |
my ($urpm, $pkgs) = @_; |
617 |
|
618 |
my @error_msgs; |
619 |
my $statusbar_msg_id; |
620 |
my $gurpm; |
621 |
local $urpm->{fatal} = sub { |
622 |
my $fatal_msg = $_[1]; |
623 |
printf STDERR "Fatal: %s\n", $fatal_msg; |
624 |
undef $gurpm; |
625 |
interactive_msg(N("Installation failed"), |
626 |
N("There was a problem during the installation:\n\n%s", $fatal_msg)); |
627 |
goto return_with_exit_code; |
628 |
}; |
629 |
local $urpm->{error} = sub { printf STDERR "Error: %s\n", $_[0]; push @error_msgs, $_[0] }; |
630 |
|
631 |
my $w = $::main_window; |
632 |
$w->set_sensitive(0); |
633 |
my $_restore_sensitive = before_leaving { $w->set_sensitive(1) }; |
634 |
|
635 |
my $_flush_guard = Gtk2::GUI_Update_Guard->new; |
636 |
|
637 |
if (my $group = get_parallel_group()) { |
638 |
return perform_parallel_install($urpm, $group, $w, \$statusbar_msg_id); |
639 |
} |
640 |
|
641 |
my $lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}) if !$::env; |
642 |
my $rpm_lock = urpm::lock::rpm_db($urpm, 'exclusive') if !$::env; |
643 |
my $state = $priority_state || $probe_only_for_updates ? { } : $urpm->{rpmdrake_state}; |
644 |
|
645 |
my $bar_id = statusbar_msg(N("Checking validity of requested packages..."), 0); |
646 |
|
647 |
# FIXME: THIS SET flag_requested on all packages!!!! |
648 |
# select packages to install / enssure selected pkg set is consistant: |
649 |
my %saved_flags; |
650 |
my $requested = { map { |
651 |
$saved_flags{$_->id} = $_->flag_requested; |
652 |
$_->id => undef; |
653 |
} grep { $_->flag_selected } @{$urpm->{depslist}} }; |
654 |
urpm::select::resolve_dependencies( |
655 |
$urpm, $state, $requested, |
656 |
rpmdb => $::env && "$::env/rpmdb.cz", |
657 |
callback_choices => \&Rpmdrake::gui::callback_choices, |
658 |
); |
659 |
statusbar_msg_remove($bar_id); |
660 |
|
661 |
my ($local_sources, $blist) = urpm::get_pkgs::selected2local_and_blists($urpm, |
662 |
$state->{selected}, |
663 |
); |
664 |
if (!$local_sources && (!$blist || !@$blist)) { |
665 |
interactive_msg( |
666 |
N("Unable to get source packages."), |
667 |
N("Unable to get source packages, sorry. %s", |
668 |
@error_msgs ? N("\n\nError(s) reported:\n%s", join("\n", @error_msgs)) : ''), |
669 |
scroll => 1, |
670 |
); |
671 |
goto return_with_exit_code; |
672 |
} |
673 |
|
674 |
my @to_install = @{$urpm->{depslist}}[keys %{$state->{selected}}]; |
675 |
my @pkgs = map { scalar($_->fullname) } sort(grep { $_->flag_selected } @to_install); |
676 |
|
677 |
@{$urpm->{ask_remove}} = sort(urpm::select::removed_packages($urpm, $urpm->{state})); |
678 |
my @to_remove = map { if_($pkgs->{$_}{selected} && !$pkgs->{$_}{pkg}->flag_upgrade, $pkgs->{$_}{urpm_name}) } keys %$pkgs; |
679 |
|
680 |
my $r = format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @to_remove); |
681 |
|
682 |
my ($size, $filesize) = $urpm->selected_size_filesize($state); |
683 |
my $install_count = int(@pkgs); |
684 |
my $to_install = $install_count == 0 ? '' : |
685 |
($priority_state ? '<b>' . N("Rpmdrake or one of its priority dependencies needs to be updated first. Rpmdrake will then restart.") . '</b>' . "\n\n" : '') . |
686 |
(P("The following package is going to be installed:", "The following %d packages are going to be installed:", $install_count, $install_count) |
687 |
. "\n\n" . format_list(map { s!.*/!!; $_ } @pkgs)); |
688 |
my $remove_count = scalar(@to_remove); |
689 |
interactive_msg(($to_install ? N("Confirmation") : N("Some packages need to be removed")), |
690 |
join("\n\n", |
691 |
($r ? |
692 |
(!$to_install ? (P("Remove one package?", "Remove %d packages?", $remove_count, $remove_count), $r) : |
693 |
(($remove_count == 1 ? |
694 |
N("The following package has to be removed for others to be upgraded:") |
695 |
: N("The following packages have to be removed for others to be upgraded:")), $r), if_($to_install, $to_install)) |
696 |
: $to_install), |
697 |
format_size($size), |
698 |
$filesize ? N("%s of packages will be retrieved.", formatXiB($filesize)) |
699 |
: (), |
700 |
N("Is it ok to continue?")), |
701 |
scroll => 1, |
702 |
yesno => 1) or return 1; |
703 |
|
704 |
my $_umount_guard = before_leaving { urpm::removable::try_umounting_removables($urpm) }; |
705 |
|
706 |
# select packages to uninstall for !update mode: |
707 |
perform_removal($urpm, { map { $_ => $pkgs->{$_} } @to_remove }) if !$probe_only_for_updates; |
708 |
|
709 |
$gurpm = Rpmdrake::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); |
710 |
my $_gurpm_clean_guard = before_leaving { undef $gurpm }; |
711 |
my $something_installed; |
712 |
|
713 |
if (@to_install && $::rpmdrake_options{auto_orphans}) { |
714 |
urpm::orphans::compute_future_unrequested_orphans($urpm, $state); |
715 |
if (my @orphans = map { scalar $_->fullname } @{$state->{orphans_to_remove}}) { |
716 |
interactive_msg(N("Orphan packages"), P("The following orphan package will be removed.", |
717 |
"The following orphan packages will be removed.", scalar(@orphans)) |
718 |
. "\n" . urpm::orphans::add_leading_spaces(join("\n", @orphans) . "\n"), scroll => 1); |
719 |
} |
720 |
} |
721 |
|
722 |
urpm::orphans::mark_as_requested($urpm, $state); |
723 |
|
724 |
my ($progress, $total, @rpms_upgrade); |
725 |
my $transaction; |
726 |
my ($progress_nb, $transaction_progress_nb, $remaining, $done); |
727 |
my $callback_inst = sub { |
728 |
my ($urpm, $type, $id, $subtype, $amount, $total) = @_; |
729 |
my $pkg = defined $id ? $urpm->{depslist}[$id] : undef; |
730 |
if ($subtype eq 'start') { |
731 |
if ($type eq 'trans') { |
732 |
$gurpm->label(1 ? N("Preparing package installation...") : N("Preparing package installation transaction...")); |
733 |
} elsif (defined $pkg) { |
734 |
$something_installed = 1; |
735 |
$gurpm->label(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$transaction_progress_nb, scalar(@{$transaction->{upgrade}})) |
736 |
. "\n" . N("Total: %s/%s", ++$progress_nb, $install_count)); |
737 |
} |
738 |
} elsif ($subtype eq 'progress') { |
739 |
$gurpm->progress($total ? $amount/$total : 1); |
740 |
} |
741 |
}; |
742 |
|
743 |
# FIXME: sometimes state is lost: |
744 |
my @ask_unselect = urpm::select::unselected_packages($urpm, $state); |
745 |
|
746 |
# fix flags for orphan computing: |
747 |
foreach (keys %{$state->{selected}}) { |
748 |
my $pkg = $urpm->{depslist}[$_]; |
749 |
$pkg->set_flag_requested($saved_flags{$pkg->id}); |
750 |
} |
751 |
my $exit_code = |
752 |
urpm::main_loop::run($urpm, $state, 1, \@ask_unselect, $requested, |
753 |
{ |
754 |
completed => sub { |
755 |
# explicitly destroy the progress window when it's over; we may |
756 |
# have sg to display before returning (errors, rpmnew/rpmsave, ...): |
757 |
undef $gurpm; |
758 |
|
759 |
undef $lock; |
760 |
undef $rpm_lock; |
761 |
}, |
762 |
inst => $callback_inst, |
763 |
trans => $callback_inst, |
764 |
ask_yes_or_no => sub { |
765 |
# handle 'allow-force' and 'allow-nodeps' options: |
766 |
my ($title, $msg) = @_; |
767 |
local $::main_window = $gurpm->{real_window}; |
768 |
interactive_msg($title, $msg, yesno => 1, scroll => 1, |
769 |
); |
770 |
}, |
771 |
message => sub { |
772 |
my ($title, $message) = @_; |
773 |
interactive_msg($title, $message, scroll => 1); |
774 |
}, |
775 |
# cancel installation when 'cancel' button is pressed: |
776 |
trans_log => sub { download_callback($gurpm, @_) or goto return_with_exit_code }, |
777 |
post_extract => sub { |
778 |
my ($set, $transaction_sources, $transaction_sources_install) = @_; |
779 |
$transaction = $set; |
780 |
$transaction_progress_nb = 0; |
781 |
$done += grep { !/\.src\.rpm$/ } values %$transaction_sources; #updates |
782 |
$total = keys(%$transaction_sources_install) + keys %$transaction_sources; |
783 |
push @rpms_upgrade, grep { !/\.src\.rpm$/ } values %$transaction_sources; |
784 |
$done += grep { !/\.src\.rpm$/ } values %$transaction_sources_install; # installs |
785 |
}, |
786 |
pre_removable => sub { |
787 |
# Gtk2::GUI_Update_Guard->new use of alarm() kill us when |
788 |
# running system(), thus making DVD being ejected and printing |
789 |
# wrong error messages (#30463) |
790 |
|
791 |
local $SIG{ALRM} = sub { die "ALARM" }; |
792 |
$remaining = alarm(0); |
793 |
}, |
794 |
|
795 |
post_removable => sub { alarm $remaining }, |
796 |
copy_removable => sub { |
797 |
my ($medium) = @_; |
798 |
interactive_msg( |
799 |
N("Change medium"), |
800 |
N("Please insert the medium named \"%s\"", $medium), |
801 |
yesno => 1, text => { no => N("Cancel"), yes => N("Ok") }, |
802 |
); |
803 |
}, |
804 |
pre_check_sig => sub { $gurpm->label(N("Verifying package signatures...")) }, |
805 |
check_sig => sub { $gurpm->progress(++$progress/$total) }, |
806 |
bad_signature => sub { |
807 |
my ($msg, $msg2) = @_; |
808 |
local $::main_window = $gurpm->{real_window}; |
809 |
$msg =~ s/:$/\n\n/m; # FIXME: to be fixed in urpmi after 2008.0 |
810 |
interactive_msg( |
811 |
N("Warning"), "$msg\n\n$msg2", yesno => 1, if_(10 < ($msg =~ tr/\n/\n/), scroll => 1), |
812 |
); |
813 |
}, |
814 |
post_download => sub { |
815 |
$canceled and goto return_with_exit_code; |
816 |
$gurpm->invalidate_cancel_forever; |
817 |
}, |
818 |
need_restart => sub { |
819 |
my ($need_restart_formatted) = @_; |
820 |
# FIXME: offer to restart the system |
821 |
interactive_msg(N("Warning"), join("\n", values %$need_restart_formatted), scroll => 1); |
822 |
}, |
823 |
trans_error_summary => sub { |
824 |
my ($nok, $errors) = @_; |
825 |
interactive_msg( |
826 |
N("Problem during installation"), |
827 |
if_($nok, N("%d installation transactions failed", $nok) . "\n\n") . |
828 |
N("There was a problem during the installation:\n\n%s", |
829 |
join("\n\n", @$errors, @error_msgs)), |
830 |
scroll => 1, |
831 |
); |
832 |
}, |
833 |
need_restart => sub { |
834 |
my ($need_restart_formatted) = @_; |
835 |
interactive_msg(N("Warning"), |
836 |
join("\n\n", values %$need_restart_formatted)); |
837 |
}, |
838 |
success_summary => sub { |
839 |
if (!($done || @to_remove)) { |
840 |
interactive_msg(N("Error"), |
841 |
N("Unrecoverable error: no package found for installation, sorry.")); |
842 |
return; |
843 |
} |
844 |
my $id = statusbar_msg(N("Inspecting configuration files..."), 0); |
845 |
my %pkg2rpmnew; |
846 |
foreach my $u (@rpms_upgrade) { |
847 |
$u =~ m|/([^/]+-[^-]+-[^-]+)\.[^\./]+\.rpm$| |
848 |
and $pkg2rpmnew{$1} = [ grep { m|^/etc| && (-r "$_.rpmnew" || -r "$_.rpmsave") } |
849 |
map { chomp_($_) } run_rpm("rpm -ql $1") ]; |
850 |
} |
851 |
statusbar_msg_remove($id); |
852 |
dialog_rpmnew(N("The installation is finished; everything was installed correctly. |
853 |
|
854 |
Some configuration files were created as `.rpmnew' or `.rpmsave', |
855 |
you may now inspect some in order to take actions:"), |
856 |
%pkg2rpmnew) |
857 |
and statusbar_msg(N("All requested packages were installed successfully."), 1); |
858 |
statusbar_msg(N("Looking for \"README\" files..."), 1); |
859 |
display_READMEs_if_needed($urpm, $w); |
860 |
}, |
861 |
already_installed_or_not_installable => sub { |
862 |
my ($msg1, $msg2) = @_; |
863 |
my $msg = join("\n", @$msg1, @$msg2); |
864 |
return if !$msg; # workaround missing state |
865 |
interactive_msg(N("Error"), $msg); |
866 |
}, |
867 |
}, |
868 |
); |
869 |
|
870 |
#- restart rpmdrake if needed, keep command line for that. |
871 |
if ($need_restart && !$exit_code && $something_installed) { |
872 |
log::explanations("restarting rpmdrake"); |
873 |
#- it seems to work correctly with exec instead of system, provided we stop timers |
874 |
#- added --previous-priority-upgrade to allow checking if yet if |
875 |
#- priority-upgrade list has changed. and make sure we don't uselessly restart |
876 |
my @argv = ('--previous-priority-upgrade=' . $urpm->{options}{'priority-upgrade'}, |
877 |
grep { !/^--no-priority-upgrade$|--previous-priority-upgrade=/ } @Rpmdrake::init::ARGV_copy); |
878 |
# remove "--emmbedded <id>" from argv: |
879 |
my $i = 0; |
880 |
foreach (@argv) { |
881 |
splice @argv, $i, 2 if /^--embedded$/; |
882 |
$i++; |
883 |
} |
884 |
alarm(0); |
885 |
# remember not to ask again questions and the like: |
886 |
writeconf(); |
887 |
exec($0, @argv); |
888 |
exit(0); |
889 |
} |
890 |
|
891 |
N("RPM transaction %d/%d"); |
892 |
N("Unselect all"); |
893 |
N("Details"); |
894 |
|
895 |
statusbar_msg_remove($statusbar_msg_id); #- XXX maybe remove this |
896 |
|
897 |
if ($exit_code == 0 && !$::rpmdrake_options{auto_orphans}) { |
898 |
if (urpm::orphans::check_unrequested_orphans_after_auto_select($urpm)) { |
899 |
if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { |
900 |
interactive_msg(N("Orphan packages"), $msg, scroll => 1); |
901 |
} |
902 |
} |
903 |
} |
904 |
|
905 |
return_with_exit_code: |
906 |
return !($something_installed || scalar(@to_remove)); |
907 |
} |
908 |
|
909 |
|
910 |
# -=-=-=---=-=-=---=-=-=-- remove packages -=-=-=---=-=-=---=-=-=- |
911 |
|
912 |
sub perform_removal { |
913 |
my ($urpm, $pkgs) = @_; |
914 |
my @toremove = map { if_($pkgs->{$_}{selected}, $pkgs->{$_}{urpm_name}) } keys %$pkgs; |
915 |
return if !@toremove; |
916 |
my $gurpm = Rpmdrake::gurpm->new(1 ? N("Please wait") : N("Please wait, removing packages..."), N("Initializing..."), transient => $::main_window); |
917 |
my $_gurpm_clean_guard = before_leaving { undef $gurpm }; |
918 |
|
919 |
my $may_be_orphans = 1; |
920 |
urpm::orphans::unrequested_orphans_after_remove($urpm, \@toremove) |
921 |
or $may_be_orphans = 0; |
922 |
|
923 |
my $progress = -1; |
924 |
local $urpm->{log} = sub { |
925 |
my $str = $_[0]; |
926 |
print $str; |
927 |
$progress++; |
928 |
return if $progress <= 0; # skip first "creating transaction..." message |
929 |
$gurpm->label($str); # display "removing package %s" |
930 |
$gurpm->progress(min(0.99, scalar($progress/@toremove))); |
931 |
gtkflush(); |
932 |
}; |
933 |
|
934 |
my @results; |
935 |
slow_func_statusbar( |
936 |
N("Please wait, removing packages..."), |
937 |
$::main_window, |
938 |
sub { |
939 |
@results = $::rpmdrake_options{parallel} |
940 |
? urpm::parallel::remove($urpm, \@toremove) |
941 |
: urpm::install::install($urpm, \@toremove, {}, {}, |
942 |
callback_report_uninst => sub { $gurpm->label($_[0]) }, |
943 |
); |
944 |
open_rpm_db('force_sync'); |
945 |
}, |
946 |
); |
947 |
if (@results) { |
948 |
interactive_msg( |
949 |
N("Problem during removal"), |
950 |
N("There was a problem during the removal of packages:\n\n%s", join("\n", @results)), |
951 |
if_(@results > 1, scroll => 1), |
952 |
); |
953 |
return 1; |
954 |
} else { |
955 |
if ($may_be_orphans && !$::rpmdrake_options{auto_orphans}) { |
956 |
if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { |
957 |
interactive_msg(N("Information"), $msg, scroll => 1); |
958 |
} |
959 |
} |
960 |
return 0; |
961 |
} |
962 |
} |
963 |
|
964 |
1; |