1 |
#!/usr/bin/perl |
2 |
|
3 |
#- Copyright (C) 2005 MandrakeSoft SA |
4 |
#- Copyright (C) 2005-2010 Mandriva SA |
5 |
|
6 |
use strict; |
7 |
|
8 |
BEGIN { #- set up a safe path and environment |
9 |
$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin"; |
10 |
delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; |
11 |
} |
12 |
|
13 |
use gurpmi; |
14 |
use gurpm::RPMProgressDialog; |
15 |
use urpm::install; |
16 |
use urpm::media; |
17 |
use urpm::signature; |
18 |
use urpm::get_pkgs; |
19 |
use urpm::msg; |
20 |
use urpm::select; |
21 |
use urpm::main_loop; |
22 |
use Gtk2; |
23 |
|
24 |
#- default options. |
25 |
our $allow_medium_change = 0; |
26 |
our $auto_select = 0; |
27 |
our $force = 0; |
28 |
our $test = 0; |
29 |
our $use_provides = 1; |
30 |
|
31 |
# For other distros w/o exception support: |
32 |
eval { Glib->enable_exceptions2 }; |
33 |
if (my $err = $@) { |
34 |
warn "Error: $err\n"; |
35 |
} |
36 |
|
37 |
#- GUI globals |
38 |
my $mainw; |
39 |
|
40 |
my @all_rpms = gurpmi::parse_command_line(); |
41 |
|
42 |
$> and fatal(N("Must be root")); |
43 |
|
44 |
#- Now, the graphical stuff. |
45 |
|
46 |
Gtk2->init; |
47 |
|
48 |
#- Initialize urpm |
49 |
|
50 |
my $urpm; |
51 |
{ |
52 |
local @ARGV = @ARGV; |
53 |
$urpm = urpm->new_parse_cmdline; |
54 |
} |
55 |
|
56 |
#- Create main window |
57 |
|
58 |
$mainw = gurpm::RPMProgressDialog->new($urpm, \&quit); |
59 |
|
60 |
#- Performs installation |
61 |
|
62 |
configure_urpm($urpm); |
63 |
|
64 |
my $state = {}; |
65 |
my %requested = $urpm->register_rpms(@all_rpms); |
66 |
if (@gurpmi::names) { |
67 |
urpm::select::search_packages($urpm, \%requested, [ @gurpmi::names ], |
68 |
use_provides => $use_provides, |
69 |
) || $force or exit 1; |
70 |
} |
71 |
|
72 |
$mainw->label(N("Preparing packages installation...")); |
73 |
|
74 |
#- return value is true if program should be restarted (in order to take care of important |
75 |
#- packages being upgraded (problably urpmi and perl-URPM, but maybe rpm too, and glibc also ?). |
76 |
my $restart_itself = urpm::select::resolve_dependencies($urpm, |
77 |
$state, |
78 |
\%requested, |
79 |
callback_choices => \&ask_choice, |
80 |
auto_select => $::auto_select, |
81 |
priority_upgrade => $urpm->{options}{'priority-upgrade'}, |
82 |
); |
83 |
my @ask_unselect = urpm::select::unselected_packages($state); |
84 |
|
85 |
# If there are some unselected packages, designate that we are going to return nonzero code. |
86 |
if (@ask_unselect) { |
87 |
my $unselect_msg = N("Some requested packages cannot be installed:\n%s", |
88 |
urpm::select::translate_why_unselected($urpm, $state, @ask_unselect)); |
89 |
$urpm::postponed_msg .= $unselect_msg . "\n"; |
90 |
$urpm::postponed_code = 17; |
91 |
} |
92 |
|
93 |
@ask_unselect |
94 |
? ask_continue(N( |
95 |
"Some requested packages cannot be installed:\n%s\nContinue installation anyway?", |
96 |
urpm::select::translate_why_unselected($urpm, $state, @ask_unselect) |
97 |
), \&do_install) |
98 |
: do_install(); |
99 |
|
100 |
$mainw->show_all; |
101 |
Gtk2->main; |
102 |
|
103 |
my ($rpm_lock, $urpmi_lock); |
104 |
|
105 |
#- Creates and configure an urpm object for this application to use. |
106 |
sub configure_urpm { |
107 |
my ($urpm) = @_; |
108 |
|
109 |
$urpm->{fatal} = sub { |
110 |
printf STDERR "%s\n", $_[1]; |
111 |
Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'error', 'ok', Locale::gettext::iconv($_[1], undef, 'UTF-8'))->run; |
112 |
quit(); |
113 |
exit $_[0]; |
114 |
}; |
115 |
$urpm->{log} = sub { printf "%s\n", $_[0] }; |
116 |
$urpm->{error} = sub { |
117 |
my ($message) = @_; |
118 |
printf STDERR "%s\n", $message; |
119 |
|
120 |
if (my $download_errors = delete $urpm->{download_errors}) { |
121 |
$message = join("\n", @$download_errors, $message); |
122 |
} |
123 |
my $nb_lines = $message =~ tr/\n/\n/; |
124 |
my $w; |
125 |
if ($nb_lines > 30 || $message =~ /^transaction is too small/) { |
126 |
$w = Gtk2::Dialog->new(N("Warning"), $mainw, [qw(modal destroy-with-parent)], N("Ok"), 'ok'); |
127 |
$w->vbox->add(my $f = Gtk2::Frame->new); |
128 |
my $sw = create_scrolled_window(my $text = Gtk2::TextView->new); |
129 |
$sw->set_border_width(2); |
130 |
$text->set_wrap_mode('word'); |
131 |
$f->add($sw); |
132 |
$text->get_buffer->set_text($message); |
133 |
$text->set_editable(0); |
134 |
$_->show foreach $f, $sw, $text; |
135 |
$w->set_size_request(400, 400); |
136 |
$w->set_default_response('ok'); |
137 |
} else { |
138 |
$w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok', $message); |
139 |
} |
140 |
$w->run; |
141 |
$w->destroy; |
142 |
}; |
143 |
urpm::select::set_priority_upgrade_option($urpm, $gurpmi::options{previous_priority_upgrade}); |
144 |
$rpm_lock = urpm::lock::rpm_db($urpm, 'exclusive'); |
145 |
$urpmi_lock = urpm::lock::urpmi_db($urpm); |
146 |
urpm::media::configure($urpm, |
147 |
media => $gurpmi::options{media}, |
148 |
searchmedia => $gurpmi::options{searchmedia}, |
149 |
update => $::update, |
150 |
); |
151 |
$urpm->{options}{'verify-rpm'} = 0 if $gurpmi::options{'no-verify-rpm'}; |
152 |
} |
153 |
|
154 |
#- Callback for choices |
155 |
sub ask_choice { |
156 |
my (undef, undef, undef, $choices) = @_; |
157 |
return $choices->[0] if $gurpmi::options{auto}; |
158 |
my $radio; |
159 |
my @radios = map { |
160 |
$radio = Gtk2::RadioButton->new_with_label( |
161 |
$radio ? $radio->get_group : undef, |
162 |
(scalar $_->fullname) . " : " . $_->summary |
163 |
. ($_->flag_installed ? N(" (to upgrade)") : '') |
164 |
. ($_->flag_upgrade ? N(" (to install)") : '') |
165 |
); |
166 |
} @$choices; |
167 |
my $d = Gtk2::Dialog->new(N("Package choice"), $mainw, [], N("_Cancel") => 0, N("_Ok") => 1); |
168 |
my $label = Gtk2::Label->new(N("One of the following packages is needed:")); |
169 |
$label->set_alignment(0.5, 0.5); |
170 |
$d->vbox->pack_start($label, 1, 1, 0); |
171 |
$d->vbox->pack_start($_, 1, 1, 0) foreach @radios; |
172 |
my $n = 0; |
173 |
$d->signal_connect(response => sub { |
174 |
if ($_[1] == 1) { #- "ok" |
175 |
foreach (@radios) { last if $_->get_active; ++$n } |
176 |
} |
177 |
$d->destroy; |
178 |
exit(1) if $_[1] == 0; #- "cancel" |
179 |
}); |
180 |
$radios[0]->set_active(1); |
181 |
$d->set_default_response(1); # defaults to ok |
182 |
$d->show_all; |
183 |
$d->run; |
184 |
$choices->[$n]; |
185 |
} |
186 |
|
187 |
sub ask_continue { |
188 |
my ($msg, $nextclosure, $o_list, $o_end_msg) = @_; |
189 |
my $vbox = Gtk2::VBox->new(0, 5); |
190 |
$vbox->pack_start(new_label($msg), 1, 1, 0); |
191 |
$urpm->{log}($msg); |
192 |
warn ">> o_list='$o_list'\n"; |
193 |
if ($o_end_msg) { |
194 |
$vbox->pack_start(new_label($o_list), 1, 1, 0); |
195 |
$vbox->pack_start(new_label($o_end_msg), 1, 1, 0); |
196 |
} |
197 |
my $continue_button = Gtk2::Button->new(but(N("_Ok"))); |
198 |
my $quit_button = Gtk2::Button->new(but(N("_Abort"))); |
199 |
$quit_button->signal_connect(clicked => sub { $urpm->{log}("=> cancel"); &quit(); exit 1 }); |
200 |
$continue_button->signal_connect(clicked => sub { $urpm->{log}("=> ok"); goto &$nextclosure }); |
201 |
add_button_box($vbox, $quit_button, $continue_button); |
202 |
$mainw->change_widget($vbox); |
203 |
# default is to continue, but according to some HIG, warning should reverse the choise and defaults to abort |
204 |
$mainw->set_focus($continue_button); # also set_default should be called but it gives a warning! |
205 |
} |
206 |
|
207 |
sub ask_continue_if_no_auto { |
208 |
my ($msg, $nextclosure, $o_list, $o_end_msg) = @_; |
209 |
if ($gurpmi::options{auto}) { |
210 |
$urpm->{log}($msg); |
211 |
$urpm->{log}("=> ok(auto)"); |
212 |
goto &$nextclosure; |
213 |
} else { |
214 |
ask_continue($msg, $nextclosure, $o_list, $o_end_msg); |
215 |
} |
216 |
} |
217 |
|
218 |
sub ask_continue_blocking { |
219 |
my ($msg) = @_; |
220 |
my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'question', 'yes-no', $msg); |
221 |
my $answer = $w->run; |
222 |
$w->destroy; |
223 |
$urpm->{log}($msg . " => " . $answer); |
224 |
exit(1) if $answer eq 'no'; |
225 |
1; |
226 |
} |
227 |
|
228 |
sub do_install { |
229 |
$mainw->label; |
230 |
my @ask_remove = urpm::select::removed_packages($state); |
231 |
@ask_remove |
232 |
? ask_continue_if_no_auto(N( |
233 |
"The following packages have to be removed for others to be upgraded:\n%s\nContinue installation anyway?", |
234 |
urpm::select::translate_why_removed($urpm, $state, @ask_remove) |
235 |
), \&do_install_2) |
236 |
: goto &do_install_2; |
237 |
} |
238 |
|
239 |
sub do_install_2 () { |
240 |
my @to_install = map { scalar $_->fullname } @{$urpm->{depslist}}[sort { $a <=> $b } keys %{$state->{selected}}]; # sorted by medium for format_selected_packages |
241 |
$urpm->{nb_install} = @to_install; |
242 |
my ($size, $filesize) = $urpm->selected_size_filesize($state); |
243 |
my $msg2 = $size >= 0 ? |
244 |
N("%s of additional disk space will be used.", formatXiB($size)) : |
245 |
N("%s of disk space will be freed.", formatXiB(-$size)); |
246 |
my $msg2_ = $filesize ? "\n" . N("%s of packages will be retrieved.", formatXiB($filesize)) . "\n" : ''; |
247 |
my $msg3 = P("Proceed with the installation of one package?", |
248 |
"Proceed with the installation of the %d packages?", |
249 |
$urpm->{nb_install}, $urpm->{nb_install}); |
250 |
@to_install > 1 |
251 |
? ask_continue_if_no_auto( |
252 |
(scalar(@to_install) == 1 ? |
253 |
N("To satisfy dependencies, the following package is going to be installed:") |
254 |
: N("To satisfy dependencies, the following packages are going to be installed:")), |
255 |
\&do_install_3, |
256 |
join("\n", @to_install), |
257 |
$msg2 . $msg2_ . $msg3) |
258 |
: goto \&do_install_3; |
259 |
} |
260 |
|
261 |
sub do_install_3 () { |
262 |
$mainw->label($mainw->title); |
263 |
my ($local_sources, $blists) = urpm::get_pkgs::selected2local_and_blists($urpm, $state->{selected}); |
264 |
$local_sources || $blists or $urpm->{fatal}(3, N("unable to get source packages, aborting")); |
265 |
|
266 |
$mainw->init_progressbar; |
267 |
|
268 |
my $exit_code; |
269 |
$exit_code = urpm::main_loop::run($urpm, $state, scalar(@gurpmi::names), \@ask_unselect, { |
270 |
bad_signature => sub { |
271 |
my ($msg, $msg2) = @_; |
272 |
$urpm->{log}("$msg\n$msg2"); |
273 |
ask_continue_blocking("$msg\n$msg2"); |
274 |
}, |
275 |
copy_removable => sub { |
276 |
#FIXME: use use udisks to wait-for/mount cdroms: |
277 |
my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok-cancel', |
278 |
N("Please insert the medium named \"%s\"", $_[0]) |
279 |
); |
280 |
my $response = $w->run; |
281 |
$w->destroy; |
282 |
exit 1 if $response eq 'cancel'; |
283 |
1; |
284 |
}, |
285 |
trans_log => \&gurpm::RPMProgressDialog::callback_download, |
286 |
post_download => sub { |
287 |
if ($mainw->canceled) { |
288 |
$exit_code = 10; |
289 |
goto return_with_exit_code; |
290 |
} |
291 |
$mainw->invalidate_cancel_forever; |
292 |
}, |
293 |
|
294 |
ask_yes_or_no => \&ask_yes_or_no, |
295 |
|
296 |
completed => sub { |
297 |
$urpmi_lock->unlock; |
298 |
$rpm_lock->unlock; |
299 |
urpm::removable::try_umounting_removables($urpm); |
300 |
my $vbox = Gtk2::VBox->new(0, 5); # FIXME: should it be change_widget??? |
301 |
my $progress_label = Gtk2::Label->new('-'); # TEST ME |
302 |
return 0 if $gurpmi::options{auto}; |
303 |
my $sw = create_scrolled_window($progress_label); |
304 |
$sw->set_size_request(500, 200); |
305 |
$vbox->pack_start($sw, 1, 1, 0); |
306 |
my $quit_button = Gtk2::Button->new(but(N("_Done"))); |
307 |
$quit_button->signal_connect(clicked => \&quit); |
308 |
add_button_box($vbox, $quit_button); |
309 |
$mainw->change_widget($vbox); |
310 |
$mainw->set_focus($quit_button); |
311 |
}, |
312 |
need_restart => sub { |
313 |
return if $gurpmi::options{auto}; |
314 |
my ($need_restart_formatted) = @_; |
315 |
my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok', |
316 |
join("\n", values %$need_restart_formatted) |
317 |
); |
318 |
$w->run; |
319 |
$w->destroy; |
320 |
}, |
321 |
missing_files_summary => sub { |
322 |
my ($error_sources) = @_; |
323 |
$mainw->set_progresslabel(N("Installation failed, some files are missing:\n%s", |
324 |
join("\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; " $_" } |
325 |
values %$error_sources)) |
326 |
. "\n" . N("You may want to update your urpmi database.")); |
327 |
}, |
328 |
trans_error_summary => sub { |
329 |
my ($_nok, $errors) = @_; |
330 |
$mainw->set_progresslabel(N("Installation failed:") . "\n" . join("\n", map { "\t$_" } @$errors)); |
331 |
}, |
332 |
# TODO: use urpmi strings: |
333 |
already_installed_or_not_installable => sub { |
334 |
my ($_msg1, $_msg2) = @_; |
335 |
$mainw->set_progresslabel(N("The package(s) are already installed")); |
336 |
}, |
337 |
success_summary => sub { $mainw->set_progresslabel(N("Installation finished")) }, |
338 |
pre_check_sig => \&gurpm::RPMProgressDialog::callback_pre_check_sig, |
339 |
uninst => \&gurpm::RPMProgressDialog::callback_inst, |
340 |
inst => \&gurpm::RPMProgressDialog::callback_inst, |
341 |
trans => \&gurpm::RPMProgressDialog::callback_inst, |
342 |
} |
343 |
); |
344 |
|
345 |
# Merge postponed exit code to the result of package installation. |
346 |
$exit_code ||= $urpm::postponed_code; |
347 |
|
348 |
#- restart gurpmi if needed, keep command line for that. |
349 |
if ($restart_itself && !$exit_code) { |
350 |
print N("restarting urpmi"), "\n"; |
351 |
#- it seems to work correctly with exec instead of system, provided |
352 |
#- added --previous-priority-upgrade to allow checking if yet if |
353 |
#- priority-upgrade list has changed. and make sure we don't uselessly restart |
354 |
@ARGV = ('--previous-priority-upgrade=' . $urpm->{options}{'priority-upgrade'}, |
355 |
grep { !/^--no-priority-upgrade$|--previous-priority-upgrade=/ } @ARGV); |
356 |
exec $0, @ARGV; |
357 |
} |
358 |
|
359 |
return_with_exit_code: |
360 |
# Show postponed message before exiting |
361 |
$urpm->{error}->($urpm::postponed_msg) if $urpm::postponed_code != 0; |
362 |
|
363 |
exit $exit_code; |
364 |
} |
365 |
|
366 |
sub ask_yes_or_no { |
367 |
my ($_title, $msg) = @_; |
368 |
# MessageDialogs have no titles unless using 'secondary-text' |
369 |
my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'yes-no', $msg); |
370 |
my $response = $w->run; |
371 |
$w->destroy; |
372 |
$response eq 'yes'; |
373 |
} |
374 |
|
375 |
|