#!/usr/bin/perl #- Copyright (C) 2005 MandrakeSoft SA #- Copyright (C) 2005-2007 Mandriva SA use strict; use warnings; BEGIN { #- set up a safe path and environment $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"; delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; } use gurpmi; use urpm::install; use urpm::media; use urpm::signature; use urpm::get_pkgs; use urpm::msg; use urpm::select; use Gtk2; #- GUI globals my ($mainw, $mainbox); #- Replaces the contents of the main window with the specified box #- (avoids popup multiplication) sub change_mainw { $mainw->remove($mainbox); ($mainbox) = @_; $mainw->add($mainbox); $mainw->show_all; } sub sync () { $mainw->show; Gtk2->main_iteration while Gtk2->events_pending; } #- sets the window to a please-wait message sub wait_label { my ($o_text) = @_; my $wait_vbox = Gtk2::VBox->new(0, 5); my $wait_label = Gtk2::Label->new($o_text || N("Please wait...")); $wait_label->set_alignment(0.5, 0.5); $wait_vbox->pack_start($wait_label, 1, 1, 0); change_mainw($wait_vbox); sync(); } my @all_rpms = gurpmi::parse_command_line(); $> and fatal(N("Must be root")); #- Now, the graphical stuff. Gtk2->init; Gtk2->croak_execeptions; #- Create main window $mainw = Gtk2::Window->new('toplevel'); $mainw->set_border_width(3); $mainw->set_title(N("RPM installation")); $mainw->signal_connect(destroy => \&quit); $mainw->set_position('center'); $mainw->set_modal(0); $mainbox = Gtk2::VBox->new(0, 5); $mainw->add($mainbox); #- Performs installation my $urpm = configure_urpm(); my $state = {}; my %requested = $urpm->register_rpms(@all_rpms); if (@gurpmi::names) { urpm::select::search_packages($urpm, \%requested, [ @gurpmi::names ]); } urpm::select::resolve_dependencies($urpm, $state, \%requested, callback_choices => \&ask_choice, auto_select => $gurpmi::options{'auto-select'}, ); my @ask_unselect = urpm::select::unselected_packages($urpm, $state); @ask_unselect ? ask_continue(N( "Some requested packages cannot be installed:\n%s\nContinue installation anyway?", urpm::select::translate_why_unselected($urpm, $state, @ask_unselect) ), \&do_install) : do_install(); $mainw->show_all; Gtk2->main; my ($rpm_lock, $urpmi_lock); #- Creates and configure an urpm object for this application to use. sub configure_urpm() { my $urpm = new urpm; $urpm->{fatal} = sub { Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'error', 'ok', Locale::gettext::iconv($_[1], undef, 'UTF-8'))->run; quit(); exit $_[0]; }; $urpm->{error} = sub { my ($message) = @_; my $nb_lines = $message =~ tr/\n/\n/; $message = Locale::gettext::iconv($message, undef, 'UTF-8'); my $w; if ($nb_lines > 30) { $w = Gtk2::Dialog->new(N("Warning"), $mainw, [qw(modal destroy-with-parent)], N("Ok"), 'ok'); $w->vbox->add(my $f = Gtk2::Frame->new); my $sw = create_scrolled_window(my $text = Gtk2::TextView->new); $sw->set_border_width(2); $f->add($sw); $text->get_buffer->set_text($message); $_->show foreach $f, $sw, $text; $w->set_size_request(400, 400); } else { $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok', $message); } $w->run; $w->destroy; }; $rpm_lock = urpm::lock::rpm_db($urpm, 'exclusive'); $urpmi_lock = urpm::lock::urpmi_db($urpm); urpm::media::configure($urpm, root => $gurpmi::options{root}, media => $gurpmi::options{media}, searchmedia => $gurpmi::options{searchmedia}, ); $urpm->{options}{'verify-rpm'} = 0 if $gurpmi::options{'no-verify-rpm'}; #- default options values exists $urpm->{options}{$_} or $urpm->{options}{$_} = 1 foreach qw(post-clean verify-rpm split-length); $urpm->{options}{'split-level'} = 20 unless exists $urpm->{options}{'split-level'}; $urpm; } #- Callback for choices sub ask_choice { my (undef, undef, undef, $choices) = @_; return $choices->[0] if $gurpmi::options{auto}; my $radio; my @radios = map { $radio = Gtk2::RadioButton->new_with_label( $radio ? $radio->get_group : undef, (scalar $_->fullname) . " : " . $_->summary . ($_->flag_installed ? N(" (to upgrade)") : '') . ($_->flag_upgrade ? N(" (to install)") : '') ); } @$choices; my $d = Gtk2::Dialog->new(N("Package choice"), $mainw, [], N("_Cancel") => 0, N("_Ok") => 1); my $label = Gtk2::Label->new(N("One of the following packages is needed:")); $label->set_alignment(0.5, 0.5); $d->vbox->pack_start($label, 1, 1, 0); $d->vbox->pack_start($_, 1, 1, 0) foreach @radios; my $n = 0; $d->signal_connect(response => sub { if ($_[1] == 1) { #- "ok" foreach (@radios) { last if $_->get_active; ++$n } } $d->destroy; exit 0 if $_[1] == 0; #- "cancel" }); $radios[0]->set_active(1); $d->show_all; $d->run; $choices->[$n]; } sub ask_continue { my ($msg, $nextclosure) = @_; my $vbox = Gtk2::VBox->new(0, 5); $vbox->pack_start(new_label($msg), 1, 1, 0); my $continue_button = Gtk2::Button->new(but(N("_Ok"))); my $quit_button = Gtk2::Button->new(but(N("_Abort"))); $quit_button->signal_connect(clicked => \&quit); $continue_button->signal_connect(clicked => sub { goto &$nextclosure }); add_button_box($vbox, $quit_button, $continue_button); change_mainw($vbox); } sub ask_continue_blocking { my ($msg) = @_; my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'question', 'yes-no', $msg); my $answer = $w->run; $w->destroy; quit() if $answer eq 'no'; } sub do_install { wait_label(); my @ask_remove = urpm::select::removed_packages($urpm, $state); @ask_remove ? ask_continue(N( "The following packages have to be removed for others to be upgraded:\n%s\nContinue installation anyway?", urpm::select::translate_why_removed($urpm, $state, @ask_remove) ), \&do_install_2) : goto &do_install_2; } sub do_install_2 () { my @to_install; my $sum; foreach my $pkg (sort { $a->name cmp $b->name } @{$urpm->{depslist}}[keys %{$state->{selected}}]) { if ($pkg->arch ne 'src') { push @to_install, scalar $pkg->fullname; $sum += $pkg->size; } } $urpm->{nb_install} = @to_install; @to_install > 1 && !$gurpmi::options{auto} ? ask_continue( ( (scalar(@to_install) == 1) ? N("To satisfy dependencies, the following package is going to be installed:") : N("To satisfy dependencies, the following packages are going to be installed:") ) . join("\n", '', @to_install, '') . P("(%d package, %d MB)", "(%d packages, %d MB)", scalar(@to_install), scalar(@to_install), toMb($sum)), , \&do_install_3) : goto \&do_install_3; } sub do_install_3 () { wait_label(N("Package installation...")); my ($local_sources, $list) = urpm::get_pkgs::selected2list($urpm, $state->{selected}); $local_sources || $list or $urpm->{fatal}(3, N("unable to get source packages, aborting")); my %sources = %$local_sources; my %error_sources; my $vbox = Gtk2::VBox->new(0, 5); my $progress_label = Gtk2::Label->new('-'); $vbox->pack_start($progress_label, 1, 1, 0); my $progressbar = Gtk2::ProgressBar->new; $progressbar->set_size_request(450, -1); $vbox->pack_start($progressbar, 0, 0, 0); change_mainw($vbox); urpm::removable::copy_packages_of_removable_media($urpm, $list, \%sources, sub { my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok-cancel', N("Please insert the medium named \"%s\" on device [%s]", $_[0], $_[1]) ); my $response = $w->run; $w->destroy; exit 0 if $response eq 'cancel'; 1; }); urpm::install::create_transaction($urpm, $state, split_level => $urpm->{options}{'split-level'}, split_length => $urpm->{options}{'split-length'}, ); my (@errors); my $progress_nb; foreach my $set (@{$state->{transaction} || []}) { my (@transaction_list, %transaction_sources); urpm::install::prepare_transaction($urpm, $set, $list, \%sources, \@transaction_list, \%transaction_sources); urpm::get_pkgs::download_packages_of_distant_media($urpm, \@transaction_list, \%transaction_sources, \%error_sources, callback => sub { my ($mode, $file, $percent) = @_; if ($mode eq 'start') { $file =~ s|/*\s*$||; $file =~ s|.*/||; $progress_label->set_label(N("Downloading package `%s'...", $file)); select(undef, undef, undef, 0.1); #- hackish } elsif ($mode eq 'progress') { $progressbar->set_fraction($percent / 100); } elsif ($mode eq 'end') { $progressbar->set_fraction(1); } sync(); }, ); my %transaction_sources_install = %{$urpm->extract_packages_to_install(\%transaction_sources, $state) || {}}; if ($urpm->{options}{'verify-rpm'} || grep { $_->{'verify-rpm'} } @{$urpm->{media}}) { my @bad_signatures = urpm::signature::check($urpm, \%transaction_sources_install, \%transaction_sources); if (@bad_signatures) { ask_continue_blocking(N( "The following packages have bad signatures:\n%s\n\nDo you want to continue installation ?", (join "\n", @bad_signatures) )); } } #- check for local files. if (my @missing = grep { m|^/| && ! -e $_ } values %transaction_sources_install, values %transaction_sources) { $urpm->{error}(N("Installation failed, some files are missing:\n%s\nYou may want to update your urpmi database", join "\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; " $_" } @missing)); next; } if (keys(%transaction_sources_install) || keys(%transaction_sources)) { @{$set->{remove} || []} and $progress_label->set_label(N("removing %s", join(' ', @{$set->{remove} || []}))); my $callback_inst = sub { my ($urpm, $type, $id, $subtype, $amount, $total) = @_; my $pkg = defined $id ? $urpm->{depslist}[$id] : undef; if ($subtype eq 'start') { if ($type eq 'trans') { $progress_label->set_label(N("Preparing...")); } else { defined $pkg and $progress_label->set_label( N("Installing package `%s' (%s/%s)...", $pkg->name, ++$progress_nb, $urpm->{nb_install}) ); } } elsif ($subtype eq 'progress') { $progressbar->set_fraction($amount / $total); } sync(); }; my @l = urpm::install::install($urpm, $set->{remove} || [], \%transaction_sources_install, \%transaction_sources, oldpackage => $state->{oldpackage}, callback_inst => $callback_inst, callback_trans => $callback_inst, #- global options that might have been read from urpmi.cfg excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}, repackage => $urpm->{options}{repackage}, nosize => $urpm->{options}{ignoresize}, ignorearch => $urpm->{options}{ignorearch}, noscripts => $urpm->{options}{noscripts}, post_clean_cache => $urpm->{options}{'post-clean'}, ); push @errors, @l; } } $vbox = Gtk2::VBox->new(0, 5); $progress_label = Gtk2::Label->new('-'); my $sw = create_scrolled_window($progress_label); $sw->set_size_request(500, 200); $vbox->pack_start($sw, 1, 1, 0); my $quit_button = Gtk2::Button->new(but(N("_Done"))); $quit_button->signal_connect(clicked => \&quit); add_button_box($vbox, $quit_button); change_mainw($vbox); if (@errors) { $progress_label->set_label(N("Installation failed:") . "\n" . join("\n", map { "\t$_" } @errors)); } elsif (values %error_sources) { $progress_label->set_label(N("Installation failed, some files are missing:\n%s\nYou may want to update your urpmi database", join "\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; " $_" } values %error_sources)); } elsif (@{$state->{transaction} || []} == 0 && @ask_unselect == 0) { $progress_label->set_label(N("The package(s) are already installed")); } else { $progress_label->set_label(N("Installation finished")); } $urpmi_lock->unlock; $rpm_lock->unlock; urpm::removable::try_umounting_removables($urpm); }