#!/usr/bin/perl -T #- Copyright (C) 1999 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 urpm; # for i18n use POSIX; use Locale::GetText; setlocale (LC_ALL, ""); Locale::GetText::textdomain ("urpmi"); import Locale::GetText I_; *_ = *I_; #- default options. my $auto = 0; my $auto_select = 0; my $force = 0; my $X = 0; my $all = 0; my $complete = 0; my $minimal = 1; my $rpm_opt = "-Uvh"; my $verbose = 0; my $uid; my @files; my @names; #my %I18N; #load_po(); $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"; delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; ($<, $uid) = ($>, $<); sub usage { #die(_("usage: urpmi [-h] [--auto] [--force] [-a] [-v] package_name|rpm_file [package_names|rpm_files...]\n")); die( sprintf (_("urpmi version %s Copyright (C) 1999, 2000, 2001 MandrakeSoft. This is free software and may be redistributed under the terms of the GNU GPL. usage: --help - print this help message. --auto - automatically select a good package in choices. --auto-select - automatically select packages for upgrading the system. --force - force invocation even if some package do not exist. --X - use X interface. --best-output - choose best interface according to the environment: X or text mode. -a - select all matches on command line. -m - choose minimum closure of requires (default). -M - choose maximun closure of requires. -c - choose complete method for resolving requires closure. -q - quiet mode. -v - verbose mode. names or rpm files (only for root) given on command line are installed. "), $urpm::VERSION)); } #- parse arguments list. my @nextargv; for (@ARGV) { /^--help$/ and do { usage; next }; /^--auto$/ and do { $auto = 1; next }; /^--auto-select$/ and do { $auto_select = 1; next }; /^--force$/ and do { $force = 1; next }; /^--X$/ and do { $X = 1; next }; /^--best-output$/ and do { $X ||= $ENV{DISPLAY} && system('/usr/X11R6/bin/xtest', '') == 0; next }; /^--comment$/ and do { push @nextargv, undef; next }; /^-(.*)$/ and do { foreach (split //, $1) { /[\?h]/ and do { usage; next }; /a/ and do { $all = 1; next }; /c/ and do { $complete = 1; next }; /m/ and do { $minimal = 1; next }; /M/ and do { $minimal = 0; next }; /q/ and do { $rpm_opt = "-U"; next }; /v/ and do { $verbose = 1; next }; die "urpmi: unknown option \"-$1\", check usage with --help\n"; } next }; @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next }; /\.rpm$/ and do { -r $_ or print STDERR "urpmi: cannot read rpm file \"$_\"\n", next; push @files, untaint($_); next }; push @names, $_; } #- log only at this point in case of query usage. log_it(scalar localtime, " urpmi called with @ARGV\n"); my ($pid_out, $pid_err); open SAVEOUT, ">&STDOUT"; select SAVEOUT; $| = 1; open SAVEERR, ">&STDERR"; select SAVEERR; $| = 1; unless ($pid_out = open STDOUT, "|-") { open F, ">>/var/log/urpmi.log"; select F; $| = 1; while () { print SAVEOUT $_; print F $_; } close F; exit 0; } unless ($pid_err = open STDERR, "|-") { open F, ">>/var/log/urpmi.log"; select F; $| = 1; while () { print SAVEERR $_; print F $_; } close F; exit 0; } select STDERR; $| = 1; # make unbuffered select STDOUT; $| = 1; # make unbuffered #- params contains informations to parse installed system. my $urpm = new urpm; #- remove verbose if not asked. $verbose or $urpm->{log} = sub {}; $urpm->read_depslist; if (@files) { $uid == 0 or die _("Only superuser is allowed to install local packages"); #- sanity check of pathname. m|^/| or $_ = "./$_" foreach @files; #- read provides file which is needed only to compute incremental #- dependancies, of files provided. $urpm->read_provides; #- build closure with local package and return list of names. push @names, $urpm->register_local_packages(@files); } #- reparse whole internal depslist to match against newer packages only. #- ignored medium MUST HAVE BEEN taken into account for building hdlist before! $urpm->relocate_depslist; #- 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 dependancy #- will be updated too. my %packages; $urpm->search_packages(\%packages, [ ($minimal ? () : ('basesystem')), @names], all => $all) or $force or exit 1; #- filter to add in packages selected required packages. my $ask_choice = sub { my ($urpm, @choices_id) = @_; my $n = 1; #- default value. my @l = map { my $info = $urpm->{params}{depslist}[$_]; "$info->{name}-$info->{version}-$info->{release}" } @choices_id; if (@l > 1 && !$auto) { my $msg = _("One of the following packages is needed:"); if ($X) { `gchooser "$msg" @l`; $n = $? >> 8 || die; } else { print SAVEOUT "$msg\n"; my $i = 0; foreach (@l) { print SAVEOUT " ", ++$i, "- $_\n"; } while (1) { printf SAVEOUT sprintf(_("What is your choice? (1-%d) "), $i); $n = ; 1 <= $n && $n <= $i and last; print SAVEOUT _("Sorry, bad choice, try again\n"); } } } $choices_id[$n - 1]; }; #- auto select package for upgrading the distribution. if ($auto_select) { my (%to_remove, %keep_files); $urpm->select_packages_to_upgrade('', \%packages, \%to_remove, \%keep_files); if (keys(%to_remove) > 0) { print STDERR "some package have to be removed for being upgraded, this is not supported yet\n"; } } if ($minimal) { $urpm->read_provides; $urpm->read_config; $urpm->filter_minimal_packages_to_upgrade(\%packages, $ask_choice); } else { $urpm->filter_packages_to_upgrade(\%packages, $ask_choice, complete => $complete); } #- get out of package that should not be upgraded. $urpm->deselect_unwanted_packages(\%packages); #- 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 = $auto_select; my $sum = 0; foreach (keys %packages) { defined $packages{$_} and $ask_user = 1; my $info = $urpm->{params}{depslist}[$_]; $sum += $info->{size}; push @to_install, "$info->{name}-$info->{version}-$info->{release}"; } if (!$auto) { if ($ask_user) { my $msg = sprintf(_("To satisfy dependencies, the following packages are going to be installed (%d MB)"), toMb($sum)); my $msg2 = _("Is it ok?"); if ($X) { my $p = join "\n", @to_install; my $ok = _("Ok"); my $cancel = _("Cancel"); `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg:\n$p\n\n$msg2"`; $? and exit 0; } else { $noexpr = _("Nn"); $yesexpr = _("Yy"); print SAVEOUT "$msg:\n@to_install\n$msg2" . _(" (Y/n) "); =~ /[$noexpr]/ and exit 0; } } } $urpm->read_config; my ($local_sources, $list) = $urpm->get_source_packages(\%packages); unless ($local_sources || $list) { die("unable to get source packages, aborting"); exit 1; } my @sources = $urpm->upload_source_packages($local_sources, $list, 'force_local', sub { my $msg = sprintf(_("Please insert the medium named \"%s\" on device [%s]"), @_); my $msg2 = _("Press enter when it's done..."); if ($X) { my $ok = _("Ok"); my $cancel = _("Cancel"); `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg"`; !$?; } else { print SAVEOUT "$msg\n$msg2 "; ; 1; } }); install(@sources); @sources or message_auto(_("everything already installed")); #- this help flushing correctly by closing this file before (piped on tee). #- but killing them is generally better. kill 15, $pid_err; close STDERR; kill 15, $pid_out; close STDOUT; sub install { @_ or return; printf SAVEOUT sprintf(_("installing %s\n"), join(' ', @_)); log_it(scalar localtime, " @_\n"); system($X ? "grpmi" : ("rpm", $rpm_opt), @_); if ($?) { message(_("Installation failed")); $X and exit 1; #- grpmi handles --nodeps and --force by itself m|^/| && !-e $_ and exit 1 foreach @_; #- missing local file $noexpr = _("Nn"); $yesexpr = _("Yy"); print SAVEOUT _("Try installation without checking dependencies? (y/N) "); $force or =~ /[$yesexpr]/ or exit 1; system("rpm", $rpm_opt, "--nodeps", @_); if ($?) { message(_("Installation failed")); print SAVEOUT _("Try installation even more strongly (--force)? (y/N) "); $force or =~ /[$yesexpr]/ or exit 0; system("rpm", $rpm_opt, "--nodeps", "--force", @_); } } } sub toMb { my $nb = $_[0] / 1024 / 1024; int $nb + 0.5; } sub message { $X ? `gmessage -default Ok -buttons Ok "$_[0]"` : print SAVEOUT "$_[0]\n"; } sub message_auto { $auto ? print SAVEOUT "$_[0]\n" : message($_[0]); } #sub _ { # my $s = shift @_; my $t = translate($s); # $t && ref $t or return sprintf $t, @_; # my ($T, @p) = @$t; # sprintf $T, @_[@p]; #} #sub translate { $I18N{$_[0]} || $_[0]; } sub log_it { local *LOG; open LOG, ">>/var/log/urpmi.log" or die "can't ouptput to log file\n"; print LOG @_; } sub untaint { my @r = (); foreach (@_) { /(.*)/; push @r, $1; } @r == 1 ? $r[0] : @r } sub load_po { my ($from, $to, $state, $fuzzy); my $lang = $ENV{LANGUAGE} || $ENV{LC_ALL} || $ENV{LC_MESSAGES} || $ENV{LANG}; my $f; -e ($f = "/usr/share/locale/$_/LC_MESSAGES/urpmi.po") and last foreach split ':', $lang; local *F; open F, $f or return; foreach () { /^msgstr/ and $state = 1; /^msgid/ && !$fuzzy and $state = 2; if (/^(#|$)/ && $state != 3) { $state = 3; s/\\n/\n/g foreach $from, $to; if (my @l = $to =~ /%(\d+)\$/g) { $to =~ s/%(\d+)\$/%/g; $to = [ $to, map { $_ - 1 } @l ]; } $I18N{$from} = $to if $from; $from = $to = ''; } $to .= (/"(.*)"/)[0] if $state == 1; $from .= (/"(.*)"/)[0] if $state == 2; $fuzzy = /^#, fuzzy/; } }