#!/usr/bin/perl # $Id$ #- Copyright (C) 1999,2001 MandrakeSoft (pixel@linux-mandrake.com) #- #- This program is free software; you can redistribute it and/or modify #- it under the terms of the GNU General Public License as published by #- the Free Software Foundation; either version 2, or (at your option) #- any later version. #- #- This program is distributed in the hope that it will be useful, #- but WITHOUT ANY WARRANTY; without even the implied warranty of #- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #- GNU General Public License for more details. #- #- You should have received a copy of the GNU General Public License #- along with this program; if not, write to the Free Software #- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #use strict qw(subs vars refs); use strict; use urpm; use MDK::Common; #- contains informations to parse installed system. my $urpm = new urpm; #- default options. my $update = 0; my $media = ''; my $excludemedia = ''; my $sortmedia = ''; my $synthesis = ''; my $allow_medium_change = 0; my $auto_select = 0; my $no_remove = 0; my $split_level = 20; my $split_length = 1; my $force = 0; my $parallel = ''; my $X = 0; my $WID = 0; my $all = 0; my $rpm_opt = "vh"; my $use_provides = 1; my $src = 0; my $install_src = 0; my $clean = 0; my $noclean = 0; my $verbose = 0; my $skip = ''; my $root = ''; my $usedistrib = 0; my $bug = ''; my $env = ''; my $log = ''; my $test = 0; my $uid; my @files; my @src_files; my @names; my @src_names; $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"; delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; ($<, $uid) = ($>, $<); $ENV{HOME} ||= "/root"; $ENV{USER} ||= "root"; sub usage { print STDERR N("urpmi version %s Copyright (C) 1999, 2000, 2001, 2002 MandrakeSoft. This is free software and may be redistributed under the terms of the GNU GPL. usage: ", $urpm::VERSION) . N(" --help - print this help message. ") . N(" --update - use only update media. ") . N(" --media - use only the given media, separated by comma. ") . N(" --excludemedia - do not use the given media, separated by comma. ") . N(" --sortmedia - sort media according to substrings separated by comma. ") . N(" --synthesis - use the given synthesis instead of urpmi db. ") . N(" --auto - automatically select a package in choices. ") . N(" --auto-select - automatically select packages to upgrade the system. ") . N(" --no-uninstall - never ask to uninstall a package, abort the installation. ") . N(" --keep - keep existing packages if possible, reject requested packages that leads to remove. ") . N(" --split-level - split in small transaction if more than given packages are going to be installed or upgraded, default is %d. ", $split_level) . N(" --split-length - small transaction length, default is %d. ", $split_length) . N(" --fuzzy - impose fuzzy search (same as -y). ") . N(" --src - next package is a source package (same as -s). ") . N(" --install-src - install only source package (no binaries). ") . N(" --clean - remove rpm from cache before anything else. ") . N(" --noclean - keep rpm not used in cache. ") . N(" --force - force invocation even if some packages do not exist. ") . N(" --allow-nodeps - allow asking user to install packages without dependencies checking. ") . N(" --allow-force - allow asking user to install packages without dependencies checking and integrity. ") . N(" --parallel - distributed urpmi accross machines of alias. ") . N(" --root - use another root for rpm installation. ") . N(" --use-distrib - configure urpmi on the fly from a distrib tree, useful to install a chroot with --root option. ") . N(" --wget - use wget to retrieve distant files. ") . N(" --curl - use curl to retrieve distant files. ") . N(" --limit-rate - limit the download speed. ") . N(" --resume - resume transfer of partially-downloaded files (--no-resume disables it, default is disabled). ") . N(" --proxy - use specified HTTP proxy, the port number is assumed to be 1080 by default (format is ). ") . N(" --proxy-user - specify user and password to use for proxy authentication (format is ). ") . N(" --bug - output a bug report in directory indicated by next arg. ") . N(" --env - use specific environment (typically a bug report). ") . N(" --X - use X interface. ") . N(" --best-output - choose best interface according to the environment: X or text mode. ") . N(" --verify-rpm - verify rpm signature before installation (--no-verify-rpm disable it, default is enabled). ") . N(" --test - verify if the installation can be achieved correctly. ") . N(" --excludepath - exclude path separated by comma. ") . N(" --excludedocs - exclude docs files. ") . N(" -a - select all matches on command line. ") . N(" -p - allow search in provides to find package. ") . N(" -P - do not search in provides to find package. ") . N(" -y - impose fuzzy search (same as --fuzzy). ") . N(" -s - next package is a source package (same as --src). ") . N(" -q - quiet mode. ") . N(" -v - verbose mode. ") . "\n" . N(" names or rpm files given on command line will be installed. "); exit(0); } sub to_utf8 { Locale::gettext::iconv($_[0], undef, "UTF-8") } sub gmessage { my ($msg, %params) = @_; my $ok = to_utf8($params{ok} || N("Ok")); my $cancel = to_utf8($params{cancel} || N("Cancel")); $ok =~ s/,/\\,/g; $cancel =~ s/,/\\,/g; my $buttons = $params{ok_only} ? "$ok:0" : "$ok:0,$cancel:2"; foreach (@{$params{add_buttons}}) { $_ =~ s/,/\\,/g; $buttons .= ",$_"; } $msg = to_utf8($msg); `gmessage -default "$ok" -buttons "$buttons" "$msg"`; } sub save_file { my ($orig) = @_; my $msg = N("Choose location to save file"); my $location = `gfilechooser "$msg"`; if (!$? && $location) { exec 'mv', '-f', $orig, $location; } exit(1); } #- parse arguments list. my @nextargv; my $command_line = join " ", @ARGV; @ARGV or usage; my @argv = @ARGV; while (defined($_ = shift @argv)) { /^--help$/ and do { usage; next }; /^--no-locales$/ and do { undef *N; undef *urpm::N; *N = *urpm::N = sub { sprintf(shift @_, @_) }; next }; /^--update$/ and do { $update = 1; next }; /^--media$/ and do { push @nextargv, \$media; next }; /^--exclude-?media$/ and do { push @nextargv, \$excludemedia; next }; /^--sort-?media$/ and do { push @nextargv, \$sortmedia; next }; /^--mediums$/ and do { push @nextargv, \$media; next }; /^--synthesis$/ and do { push @nextargv, \$synthesis; next }; /^--auto$/ and do { $urpm->{options}{auto} = 1; next }; /^--allow-medium-change$/ and do { $allow_medium_change = 1; next }; /^--auto-select$/ and do { $auto_select = 1; next }; /^--no-(remove|uninstall)$/ and do { $no_remove = 1; next }; /^--keep$/ and do { $urpm->{options}{keep} = 1; next }; /^--split-level$/ and do { push @nextargv, \$urpm->{options}{'split-level'}; next }; /^--split-length$/ and do { push @nextargv, \$urpm->{options}{'split-length'}; next }; /^--(no-)?fuzzy$/ and do { $urpm->{options}{fuzzy} = !$1; next }; /^--src$/ and do { $src = 1; next }; /^--install-src$/ and do { $install_src = 1; next }; /^--clean$/ and do { $clean = 1; $noclean = 0; next }; /^--noclean$/ and do { $clean = $urpm->{options}{'pre-clean'} = $urpm->{options}{'post-clean'} = 0; $noclean = 1; next }; /^--(no-)?pre-clean$/ and do { $urpm->{options}{'pre-clean'} = !$1; next }; /^--(no-)?post-clean$/ and do { $urpm->{options}{'post-clean'} = !$1; next }; /^--no-priority-upgrade$/ and do { $urpm->{options}{'priority-upgrade'} = ''; next }; /^--force$/ and do { $force = 1; next }; /^--allow-nodeps$/ and do { $urpm->{options}{'allow-nodeps'} = 1; next }; /^--allow-force$/ and do { $urpm->{options}{'allow-force'} = 1; next }; /^--parallel$/ and do { push @nextargv, \$parallel; next }; /^--wget$/ and do { $urpm->{options}{downloader} = 'wget'; next }; /^--curl$/ and do { $urpm->{options}{downloader} = 'curl'; next }; /^--limit-rate$/ and do { push @nextargv, \$urpm->{options}{'limit-rate'}; next }; /^--(no-)?resume$/ and do { $urpm->{options}{'resume'} = !$1; next }; /^--proxy$/ and do { my ($proxy, $port) = ($_ = shift @argv) =~ m,^(?:http://)?([^:]+(:\d+)?)/*$, or die N("bad proxy declaration on command line\n"); $proxy .= ":1080" unless $port; $urpm->{proxy}{http_proxy} = "http://$proxy"; next; }; /^--proxy-user$/ and do { ($_ = shift @argv) =~ /(.+):(.+)/, or die N("bad proxy declaration on command line\n"); @{$urpm->{proxy}}{qw(user proxy)} = ($1, $2); next; }; /^--bug$/ and do { push @nextargv, \$bug; next }; /^--env$/ and do { push @nextargv, \$env; next }; /^--X$/ and do { $X = 1; next }; /^--WID=(.*)$/ and do { $WID = $1; next }; /^--WID$/ and do { push @nextargv, \$WID; next }; /^--best-output$/ and do { $X ||= $ENV{DISPLAY} && system('/usr/X11R6/bin/xtest', '') == 0; next }; /^--(no-)?verify-rpm$/ and do { $urpm->{options}{'verify-rpm'} = !$1; next }; /^--(no-)?test$/ and do { $test = !$1; next }; /^--comment$/ and do { push @nextargv, undef; next }; /^--skip$/ and do { push @nextargv, \$skip; next }; /^--root$/ and do { push @nextargv, \$root; next }; /^--use-distrib$/ and do { push @nextargv, \$usedistrib; next }; /^--exclude-?path$/ and do { $urpm->{options}{excludepath} = undef; push @nextargv, \$urpm->{options}{excludepath}; next }; /^--exclude-?docs$/ and do { $urpm->{options}{excludedocs} = 1; next }; /^-(.*)$/ and do { foreach (split //, $1) { /[\?h]/ and do { usage; next }; /a/ and do { $all = 1; next }; /c/ and do { next }; /m/ and do { next }; /M/ and do { next }; #- nop /q/ and do { --$verbose; $rpm_opt = ""; next }; /p/ and do { $use_provides = 1; next }; /P/ and do { $use_provides = 0; next }; /y/ and do { $urpm->{options}{fuzzy} = 1; next }; /s/ and do { $src = 1; next }; /v/ and do { ++$verbose; $rpm_opt = "vh"; next }; /z/ and do { $urpm->{options}{compress} = 1; next }; die N("urpmi: unknown option \"-%s\", check usage with --help\n", $1) } next }; @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next }; if (/\.rpm$/) { if (/\.src\.rpm$/) { push @src_files, $_; } else { push @files, untaint($_); } next; } if (/\.urpmi$/) { push @argv, split /\n/, cat_($_); next; } if ($src) { push @src_names, $_; } else { push @names, $_; } $src = 0; #- reset switch for next package. } #- use install_src to promote all names as src package. if ($install_src) { @files and $urpm->{fatal}(1, N("What can be done with binary rpm files when using --install-src")); push @src_names, @names; @names = (); } if ($X && @src_files == 1 && @files == 0 && @names == 0 && @src_names == 0 && !$force) { gmessage( N("You have selected a source package: %s You probably didn't want to install it on your computer (installing it would allow you to make modifications to its sourcecode then compile it). What would you like to do?", $src_files[0]), ok => N("Do nothing"), cancel => N("Yes, really install it"), add_buttons => [ N("Save file") . ':1' ]); my $rc = $? >> 8; if ($rc == 0) { exit(1); } elsif ($rc == 1) { save_file($src_files[0]); } } if ($X && @files == 1 && @names == 0 && $verbose >= 0) { gmessage( N("You are about to install the following software package on your computer: %s You may prefer to just save it. What is your choice?", $files[0]), ok => N("Install it"), cancel => N("Save file")); $? and save_file($files[0]); } #- prepare bug report. if ($bug) { mkdir $bug or $urpm->{fatal}(8, (-d $bug ? N("Directory [%s] already exists, please use another directory for bug report or delete it") : N("Unable to create directory [%s] for bug report"), $bug)); #- copy all synthesis file used, along with configuration of urpmi system("cp", "-af", $urpm->{skiplist}, $urpm->{instlist}, $bug); #- allow log file. $log = "$bug/urpmi.log"; } if ($env) { print STDERR N("using specific environment on %s\n", $env); $log = "$env/urpmi_env.log"; unlink $log; #- setting new environment. $urpm->{config} = "$env/urpmi.cfg"; $urpm->{skiplist} = "$env/skip.list"; $urpm->{instlist} = "$env/inst.list"; $urpm->{statedir} = $env; } else { if ($uid > 0) { #- need to be root if binary rpms are to be installed $auto_select || @names || @files and $urpm->{fatal}(1, N("Only superuser is allowed to install packages")); } else { #- allow log if not defined. $log ||= "/var/log/urpmi.log"; } } my ($pid_out, $pid_err); if ($log) { #- log only at this point in case of query usage. log_it(scalar localtime, " urpmi called with $command_line\n"); open SAVEOUT, ">&STDOUT"; select SAVEOUT; $| = 1; open SAVEERR, ">&STDERR"; select SAVEERR; $| = 1; unless ($pid_out = open STDOUT, "|-") { my $buf_r; while () { open F, ">>$log"; select F; $| = 1; select SAVEOUT; $| = 1; $/ = \1; print SAVEOUT $_; print F $_; close F; } exit 0; } unless ($pid_err = open STDERR, "|-") { my $buf_r; while () { open F, ">>$log"; select F; $| = 1; select SAVEERR; $| = 1; $/ = \1; print SAVEERR $_; print F $_; close F; } exit 0; } select STDERR; $| = 1; # make unbuffered select STDOUT; $| = 1; # make unbuffered } else { open SAVEOUT, ">&STDOUT"; select SAVEOUT; $| = 1; open SAVEERR, ">&STDERR"; select SAVEERR; $| = 1; } #- remove verbose if not asked. unless ($bug) { $urpm->{fatal} = sub { printf SAVEERR "%s\n", $_[1]; exit($_[0]) }; $urpm->{error} = sub { printf SAVEERR "%s\n", $_[0] }; $urpm->{log} = sub { printf SAVEERR "%s\n", $_[0] }; } $verbose > 0 or $urpm->{log} = sub {}; unless ($env) { $urpm->exlock_rpm_db; $urpm->shlock_urpmi_db; } $urpm->configure(nocheck_access => $env || $uid > 0, media => $media, excludemedia => $excludemedia, sortmedia => $sortmedia, synthesis => $synthesis, update => $update, skip => $skip, root => $root, bug => $bug, parallel => $parallel, usedistrib => $usedistrib, ); #- get back activated default values of boolean options. foreach (qw(post-clean verify-rpm)) { exists $urpm->{options}{$_} or $urpm->{options}{$_} = 1; } exists $urpm->{options}{'split-level'} or $urpm->{options}{'split-level'} = $split_level; exists $urpm->{options}{'split-length'} or $urpm->{options}{'split-length'} = $split_length; exists $urpm->{options}{'priority-ugrade'} or $urpm->{options}{'priority-upgrade'} = 'rpm,perl-URPM,urpmi'; my $state = {}; my %requested = $urpm->register_rpms(@files, @src_files); #- finish bug environment creation. if ($bug) { require URPM::Build; foreach (@{$urpm->{media}}) { #- take care of virtual medium this way. $_->{hdlist} ||= "hdlist.$_->{name}.cz"; # Why to delete this flag ? This break rpm find process if list is need # delete $_->{virtual}; #- now build directly synthesis file, this is by far the simplest method. if (defined $_->{start} && defined $_->{end}) { $urpm->build_synthesis(start => $_->{start}, end => $_->{end}, synthesis => "$bug/synthesis.$_->{hdlist}"); $urpm->{log}(N("built hdlist synthesis file for medium \"%s\"", $_->{name})); } } #- fake configuration written to convert virtual media on the fly. $urpm->{config} = "$bug/urpmi.cfg"; $urpm->write_config; #- handle local packages, copy them directly in bug environment. foreach (keys %requested) { $urpm->{source}{$_} and system "cp", "-af", $urpm->{source}{$_}, $bug; } } #- search the packages according the selection given by the user, #- basesystem is added to the list so if it need to be upgraded, #- all its dependency will be updated too. #- make sure basesystem exists before. if (@names) { $urpm->search_packages(\%requested, [ @names ], all => $all, use_provides => $use_provides, fuzzy => $urpm->{options}{fuzzy}) || $force or exit 1; } if (@src_names) { $urpm->search_packages(\%requested, [ @src_names ], all => $all, use_provides => $use_provides, fuzzy => $urpm->{options}{fuzzy}, src => 1) || $force or exit 1; } #- filter to add in packages selected required packages. sub ask_choice { my ($urpm, $db, $state, $choices) = @_; my $n = 1; #- default value. my (@l) = map { scalar $_->fullname } @$choices; my $from; if (@l > 1 && !$urpm->{options}{auto}) { my $msg = (defined $from ? N("One of the following packages is needed to install %s:", $from) : N("One of the following packages is needed:")); if ($X) { `gchooser "$msg" @l`; $n = $? >> 8 || die; } else { message($msg); my $i = 0; foreach (@l) { message(" " . ++$i . "- $_") } $n = message_input(N("What is your choice? (1-%d) ", $i), undef, range => $i); defined $n or exit 1; } } $choices->[$n - 1]; }; if ($parallel && $X) { #- do it early, we'll have ui outputs while resolving deps require gurpm; gurpm::init(to_utf8(N("Package installation...")), to_utf8(N("Initializing..."))); $urpm->{ui} = { msg => sub { gurpm::label(to_utf8($_[0])) }, progress => sub { gurpm::progress($_[0]) } }; } #- do the resolution of dependencies between requested package (and auto selection if any). #- handle parallel option if any. #- return value is true if program should be restarted (in order to take care of important #- packages being upgraded (problably urpmi and perl-URPM, but maybe rpm too, and glibc also ?). my $restart_itself = $urpm->resolve_dependencies($state, \%requested, rpmdb => $env && "$env/rpmdb.cz", auto_select => $auto_select, callback_choices => \&ask_choice, install_src => $install_src, keep => $urpm->{options}{keep}, nodeps => $urpm->{options}{'allow-nodeps'} || $urpm->{options}{'allow-force'}, priority_upgrade => !$test && !$env && $urpm->{options}{'priority-upgrade'}, ); my @ask_unselect = $urpm->unselected_packages($state); if (@ask_unselect) { my $list = join "\n", $urpm->translate_why_unselected($state, sort @ask_unselect); my $msg = N("Some package requested cannot be installed:\n%s", $list); if ($urpm->{options}{auto}) { message($msg, 'noX'); } else { if ($X) { gmessage("$msg\n".N("do you agree ?")); $? and exit 0; } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); message_input($msg . N(" (Y/n) "), $force && $yesexpr, boolean => 1) =~ /[$noexpr]/ and exit 0; } } } my @ask_remove = $urpm->{options}{'allow-force'} ? @{[]} : $urpm->removed_packages($state); if (@ask_remove) { my $list = join "\n", $urpm->translate_why_removed($state, sort @ask_remove); if ($no_remove && !$force) { my $msg = N("The installation cannot continue because the following packages have to be removed for others to be upgraded:\n%s\n", $list); message($msg); exit 0; } my $msg = N("The following packages have to be removed for others to be upgraded:\n%s", $list); if ($urpm->{options}{auto}) { message($msg, 'noX'); } else { if ($X) { gmessage("$msg\n".N("do you agree ?")); $? and exit 0; } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); message_input($msg . N(" (y/N) "), $force && $yesexpr, boolean => 1) =~ /[$yesexpr]/ or exit 0; } } } #- package to install as a array of strings. my @to_install; #- check if there is at least one package to install that #- has not been given by the user. my $ask_user = $env; my $sum = 0; my @root_only; foreach my $pkg (sort { $a->name cmp $b->name } @{$urpm->{depslist}}[keys %{$state->{selected}}]) { #- reflect change in flag usage, now requested is set whatever a package is selected or not, #- but required is always set (so a required but not requested is a pure dependencies). $ask_user ||= !$pkg->flag_requested || $auto_select || $parallel; my $fullname = $pkg->fullname; if (!$env && $install_src && $pkg->arch ne 'src') { push @root_only, $fullname; } elsif ($install_src || $pkg->arch ne 'src') { $sum += $pkg->size; push @to_install, $fullname; } } if ($env) { my $msg = N("To satisfy dependencies, the following packages are going to be installed (%d MB)", toMb($sum)); my $p = join "\n", @to_install; print STDERR "$msg:\n$p\n"; exit 0; #- exit now for specific environment. } if (@root_only) { print STDERR N("You need to be root to install the following dependencies:\n%s\n", join ' ', @root_only); exit 1; } elsif (!$urpm->{options}{auto} && ($ask_user || $X) && @to_install) { my $msg = N("To satisfy dependencies, the following packages are going to be installed (%d MB)", toMb($sum)); my $msg2 = N("Is this OK?"); my $p = join "\n", @to_install; if ($X) { gmessage("$msg:\n$p\n\n$msg2"); $? and exit 0; } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); message_input("$msg:\n$p\n$msg2" . N(" (Y/n) "), $force && $yesexpr, boolean => 1) =~ /[$noexpr]/ and exit 0; } } #- if not root, the list become invisible and no download will be possible. my ($local_sources, $list) = $urpm->get_source_packages($state->{selected}, clean_all => $clean, clean_other => !$noclean && $urpm->{options}{'pre-clean'}); unless ($local_sources || $list) { $urpm->{fatal}(3, N("unable to get source packages, aborting")); } if ($X && !$parallel) { #- for $parallel, already done require gurpm; gurpm::init(to_utf8(N("Package installation...")), to_utf8(N("Initializing..."))); } my %sources = %$local_sources; my %error_sources; $urpm->copy_packages_of_removable_media($list, \%sources, verbose => $verbose > 0, force_local => 1, ask_for_medium => (!$urpm->{options}{auto} || $allow_medium_change) && sub { my $msg = N("Please insert the medium named \"%s\" on device [%s]", @_); my $msg2 = N("Press Enter when ready..."); if ($X) { $msg =~ s/"/\\"/g; gmessage($msg); !$?; } else { defined message_input("$msg\n$msg2 "); } }); #- now create transaction just before installation, this will save user impression of slowness. #- split of transaction should be disabled if --test is used. $urpm->create_transaction($state, nodeps => $urpm->{options}{'allow-nodeps'} || $urpm->{options}{'allow-force'}, split_level => $urpm->{options}{'split-level'}, split_length => !$test && $urpm->{options}{'split-length'}); my ($ok, $nok) = (0, 0); my @errors; foreach my $set (@{$state->{transaction} || []}) { my (@transaction_list, %transaction_sources); #- put a blank line to separate with previous transaction or user question. message("", 'noX'); #- prepare transaction... $urpm->prepare_transaction($set, $list, \%sources, \@transaction_list, \%transaction_sources); #- first, filter out what is really needed to download for this small transaction. $urpm->download_packages_of_distant_media(\@transaction_list, \%transaction_sources, \%error_sources, verbose => $verbose > 0, limit_rate => $urpm->{options}{'limit-rate'}, compress => $urpm->{options}{compress}, resume => $urpm->{options}{'resume'}, force_local => 1, callback => sub { my ($mode, $file, $percent, $total, $eta, $speed) = @_; if ($X) { if ($mode eq 'start') { $file =~ s|/*\s*$||; $file =~ s|.*/||; gurpm::label(to_utf8(N("Downloading package `%s'...", $file))); } elsif ($mode eq 'progress') { gurpm::progress($percent/100); } elsif ($mode eq 'end') { gurpm::progress(1); } } else { if ($mode eq 'start') { print STDERR " $file\n"; #- allow pass-protected url to be logged. } elsif ($mode eq 'progress') { my $text; if (defined $total && defined $eta) { $text = N(" %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed); } else { $text = N(" %s%% completed, speed = %s", $percent, $speed); } print SAVEERR $text, " " x (79 - length($text)), "\r"; } elsif ($mode eq 'end') { print SAVEERR " " x 79, "\r"; } elsif ($mode eq 'error') { #- error is 3rd argument, saved in $percent print STDERR N("...retrieving failed: %s"), $percent, "\n"; } } }, ); my %transaction_sources_install = %{$urpm->extract_packages_to_install(\%transaction_sources) || {}}; if ($urpm->{options}{'verify-rpm'} && !$force) { my @bad_signatures = $urpm->check_sources_signatures(\%transaction_sources_install, \%transaction_sources, translate => 1); if (@bad_signatures) { my $msg = N("The following packages have bad signatures"); my $msg2 = N("Do you want to continue installation ?"); my $p = join "\n", @bad_signatures; if ($urpm->{options}{auto}) { message("$msg:\n$p\n", 'noX'); exit 1; } else { if ($X) { gmessage("$msg:\n$p\n\n$msg2"); $? and exit 1; } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); message_input("$msg:\n$p\n$msg2" . N(" (y/N) "), $force && $yesexpr, boolean => 1) =~ /[$yesexpr]/ or exit 1; } } } } #- check for local files. if (my @missing = grep { m|^/| && ! -e $_ } values %transaction_sources_install, values %transaction_sources) { message(N("Installation failed, some files are missing:\n%s\nYou may want to update your urpmi database", join "\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; " $_" } @missing)); ++$nok; next; } #- install source package only (whatever the user is root or not, but use rpm for that). if ($install_src) { if (my @l = grep { /\.src\.rpm$/ } values %transaction_sources_install, values %transaction_sources) { system("rpm", "-i$rpm_opt", @l, ($root ? ("--root", $root) : @{[]})); $? and message(N("Installation failed")), ++$nok; } next; } #- clean to remove any src package now. foreach (\%transaction_sources_install, \%transaction_sources) { foreach my $id (keys %$_) { my $pkg = $urpm->{depslist}[$id] or next; $pkg->arch eq 'src' and delete $_->{$id}; } } if (%transaction_sources_install || %transaction_sources) { if ($parallel) { message(N("distributing %s", join(' ', values %transaction_sources_install, values %transaction_sources)), 'noX'); #- no remove are handle here, automatically done by each distant node. $urpm->{log}("starting distributed install"); $urpm->parallel_install([ keys %{$state->{rejected} || {}} ], \%transaction_sources_install, \%transaction_sources, test => $test, excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}); } else { %transaction_sources_install || %transaction_sources and message(N("installing %s", join(' ', values %transaction_sources_install, values %transaction_sources)), 'noX'); @{!$urpm->{options}{'allow-force'} && $set->{remove} || []} and message(N("removing %s", join(' ', @{!$urpm->{options}{'allow-force'} && $set->{remove} || []})), 'noX'); log_it(scalar localtime, " ", join(' ', values %transaction_sources_install, values %transaction_sources), "\n"); $urpm->{log}("starting installing packages"); my $progress_nb; my $total_nb = grep { m|^/| } values %transaction_sources_install, values %transaction_sources; my $callback_inst = $X && sub { my ($urpm, $type, $id, $subtype, $amount, $total) = @_; my $pkg = defined $id && $urpm->{depslist}[$id]; if ($subtype eq 'start') { if ($type eq 'trans') { gurpm::label(to_utf8(N("Preparing..."))); } else { gurpm::label(to_utf8(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$progress_nb, $total_nb))); } } elsif ($subtype eq 'progress') { gurpm::progress($amount/$total); } }; my @l = $urpm->install(!$urpm->{options}{'allow-force'} && $set->{remove} || [], \%transaction_sources_install, \%transaction_sources, fork => @{$state->{transaction} || []} > 1, #- fork if multiple transaction translate_message => 1, oldpackage => $state->{oldpackage}, post_clean_cache => $urpm->{options}{'post-clean'}, test => $test, excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}, callback_inst => $callback_inst, callback_trans => $callback_inst); if (@l) { message(N("Installation failed") . ":\n" . join("\n", map { "\t$_" } @l)); if ($urpm->{options}{auto} || !$urpm->{options}{'allow-nodeps'} && !$urpm->{options}{'allow-force'}) { ++$nok; push @errors, @l; } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); message_input(N("Try installation without checking dependencies? (y/N) "), $force && $yesexpr, boolean => 1) =~ /[$yesexpr]/ or ++$nok, next; $urpm->{log}("starting installing packages without deps"); @l = $urpm->install(!$urpm->{options}{'allow-force'} && $set->{remove} || [], \%transaction_sources_install, \%transaction_sources, fork => @{$state->{transaction} || []} > 1, #- fork if multiple transaction translate_message => 1, nodeps => 1, oldpackage => $state->{oldpackage}, post_clean_cache => $urpm->{options}{'post-clean'}, test => $test, excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}); if (@l) { message(N("Installation failed") . ":\n" . join("\n", map { "\t$_" } @l)); if (!$urpm->{options}{'allow-force'}) { ++$nok; push @errors, @l; } else { message_input(N("Try installation even more strongly (--force)? (y/N) "), $force && $yesexpr, boolean => 1) =~ /[$yesexpr]/ or ++$nok, next; $urpm->{log}("starting force installing packages without deps"); @l = $urpm->install(!$urpm->{options}{'allow-force'} && $set->{remove} || [], \%transaction_sources_install, \%transaction_sources, fork => @{$state->{transaction} || []} > 1, #- fork if multiple transaction translate_message => 1, nodeps => 1, force => 1, oldpackage => $state->{oldpackage}, post_clean_cache => $urpm->{options}{'post-clean'}, test => $test, excludepath => $urpm->{options}{excludepath}, excludedocs => $urpm->{options}{excludedocs}); if (@l) { message(N("Installation failed") . ":\n" . join("\n", map { "\t$_" } @l)); ++$nok; push @errors, @l; } else { ++$ok; } } } else { ++$ok; } } } else { ++$ok; } } } } $X and gurpm::end(); #- keep a track of error code. my $exit_code = 0; if (values %error_sources) { message(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)); $exit_code = 10; } if ($nok) { $nok > 1 and message(N("%d installation transactions failed", $nok) . (@errors && ":\n" . join("\n", map { "\t$_" } @errors))); if ($exit_code) { $exit_code = $ok ? 13 : 14; } else { $exit_code = $ok ? 11 : 12; } } else { if ($test) { message(N("Installation is possible")); } elsif (@names || @src_names || @files || @src_files || $auto_select) { @{$state->{transaction} || []} == 0 && @ask_unselect == 0 && $verbose >= 0 and message(N("Everything already installed"), $urpm->{options}{auto}); } } unless ($env) { $urpm->unlock_urpmi_db; $urpm->unlock_rpm_db; #- try to umount removable device which may have been mounted. $urpm->try_umounting_removables; } #- restart urpmi if needed, keep command line for that. if ($restart_itself && !$exit_code) { message(N("restarting urpmi"), 'noX'); #- it seems to work correctly with exec instead of system, provided #- STDOUT or STDERR are not closed before (else no output at all). #- added --no-priority-upgrade to make sure no restart will be done after this one. #- renamed bug report dir as /restarted to avoid exit because it alllreday exists #- This permit to have in same dir bug report before and after the restart my @arg = ($ARGV[0], map { $ARGV[$_] . ($ARGV[$_ - 1] =~ /^--bug$/ ? "/restarted" : "") } (1 .. $#ARGV)); exec "$0", '--no-priority-upgrade', @arg; } #- this help flushing correctly by closing this file before (piped on tee). #- but killing them is generally better. if ($pid_err && $pid_out) { kill 15, $pid_err, $pid_out; close STDERR; close STDOUT; } POSIX::_exit $exit_code; sub toMb { my $nb = $_[0] / 1024 / 1024; int $nb + 0.5; } sub log_it { #- if invoked as a simple user, nothing should be logged. if ($log) { local *LOG; open LOG, ">>$log" or die "can't output to log file\n"; print LOG @_; } } #- message functions. sub message { my ($msg, $noX) = @_; if ($X && !$noX && !$urpm->{options}{auto}) { gmessage($msg, ok_only => 1); $bug and log_it($msg); } else { if ($bug) { print STDOUT "$msg\n"; } else { print SAVEOUT "$msg\n"; } } } sub message_input { my ($msg, $default_input, %options) = @_; my $input; if ($X && !$default_input) { #- if a default input is given, the user doesn't have to choose (and being asked). gmessage($msg, ok_only => 1); $bug and log_it($msg); } else { my $noexpr = N("Nn"); my $yesexpr = N("Yy"); while (1) { if ($bug) { print STDOUT $msg; } else { print SAVEOUT $msg; } if ($default_input) { $bug and log_it($input); return $default_input; } $input = ; defined $input or return undef; $bug and log_it($input); if ($options{boolean}) { $input =~ /^[$noexpr$yesexpr]*$/ and last; } elsif ($options{range}) { 1 <= $input && $input <= $options{range} and last; } else { last; } message(N("Sorry, bad choice, try again\n")); } } return $input; } sub untaint { my @r; foreach (@_) { /(.*)/; push @r, $1; } @r == 1 ? $r[0] : @r }