From f87605f8e27a17aa146acd6bed0f36d394bca78d Mon Sep 17 00:00:00 2001 From: Francois Pons Date: Wed, 18 Dec 2002 15:52:44 +0000 Subject: 4.2-1mdk --- urpm.pm | 185 +++++++++++++++++++++++++++++++++++++++++++-- urpm/parallel_ka_run.pm | 70 ++++++++++++++++- urpm/parallel_ssh.pm | 71 +++++++++++++++++- urpme | 195 ++++++++++++++---------------------------------- urpmi.spec | 16 ++-- 5 files changed, 378 insertions(+), 159 deletions(-) diff --git a/urpm.pm b/urpm.pm index ff42826e..7944930b 100644 --- a/urpm.pm +++ b/urpm.pm @@ -4,7 +4,7 @@ use strict; use vars qw($VERSION); use base 'URPM'; -$VERSION = '4.1'; +$VERSION = '4.2'; =head1 NAME @@ -604,10 +604,12 @@ sub configure { } if ($options{synthesis}) { - #- synthesis take precedence over media, update options. - $options{media} || $options{update} || $options{parallel} and - $urpm->{fatal}(1, _("--synthesis cannot be used with --media, --update or --parallel")); - $urpm->parse_synthesis($options{synthesis}); + if ($options{synthesis} ne 'none') { + #- synthesis take precedence over media, update options. + $options{media} || $options{update} || $options{parallel} and + $urpm->{fatal}(1, _("--synthesis cannot be used with --media, --update or --parallel")); + $urpm->parse_synthesis($options{synthesis}); + } } else { $urpm->read_config(%options); if ($options{media}) { @@ -2069,6 +2071,8 @@ sub download_source_packages { if ($url =~ /^(removable[^:]*|file):\/(.*\.rpm)$/) { if (-r $2) { $sources{$i} = $2; + } else { + $error_sources{$i} = $2; } } elsif ($url =~ /^([^:]*):\/(.*\/([^\/]*\.rpm))$/) { if ($options{force_local} || $1 ne 'ftp' && $1 ne 'http') { #- only ftp and http protocol supported by grpmi. @@ -2249,4 +2253,175 @@ sub parallel_install { $urpm->{parallel_handler}->parallel_install(@_); } +#- find packages to remove. +sub find_packages_to_remove { + my ($urpm, $state, $l, %options) = @_; + + if ($urpm->{parallel_handler}) { + #- invoke parallel finder. + $urpm->{parallel_handler}->parallel_find_remove($urpm, $state, $l, %options, find_packages_to_remove => 1); + } else { + my $db = URPM::DB::open($options{root}); + my (@m, @notfound); + + if (!$options{matches}) { + foreach (@$l) { + my ($n, $found); + + #- check if name-version-release may have been given. + if (($n) = /^(.*)-[^\-]*-[^\-]*\.[^\.\-]*$/) { + $db->traverse_tag('name', [ $n ], sub { + my ($p) = @_; + $p->fullname eq $_ or return; + $urpm->resolve_closure_ask_remove($db, $state, $p); + push @m, join('-', ($p->fullname)[0..2]); + $found = 1; + }); + $found and next; + } + + #- check if name-version-release may have been given. + if (($n) = /^(.*)-[^\-]*-[^\-]*$/) { + $db->traverse_tag('name', [ $n ], sub { + my ($p) = @_; + join('-', ($p->fullname)[0..2]) eq $_ or return; + $urpm->resolve_closure_ask_remove($db, $state, $p); + push @m, join('-', ($p->fullname)[0..2]); + $found = 1; + }); + $found and next; + } + + #- check if name-version may have been given. + if (($n) = /^(.*)-[^\-]*$/) { + $db->traverse_tag('name', [ $n ], sub { + my ($p) = @_; + join('-', ($p->fullname)[0..1]) eq $_ or return; + $urpm->resolve_closure_ask_remove($db, $state, $p); + push @m, join('-', ($p->fullname)[0..2]); + $found = 1; + }); + $found and next; + } + + #- check if only name may have been given. + $db->traverse_tag('name', [ $_ ], sub { + my ($p) = @_; + $p->name eq $_ or return; + $urpm->resolve_closure_ask_remove($db, $state, $p); + push @m, join('-', ($p->fullname)[0..2]); + $found = 1; + }); + $found and next; + + push @notfound, $_; + } + if (@notfound && ($options{auto} || @$l > 1)) { + $options{callback_notfound} and $options{callback_notfound}->($urpm, @notfound) + or return (); + } + } + if ($options{matches} || @notfound) { + my $match = join "|", map { quotemeta } @$l; + + #- reset what has been already found. + %$state = (); + @m = (); + + #- search for package that matches, and perform closure again. + $db->traverse(sub { + my ($p) = @_; + $p->fullname =~ /$match/ or return; + $urpm->resolve_closure_ask_remove($db, $state, $p); + push @m, join('-', ($p->fullname)[0..2]); + }); + + if (@notfound) { + unless (@m) { + $options{callback_notfound} and $options{callback_notfound}->($urpm, @notfound) + or return (); + } else { + $options{callback_fuzzy} and $options{callback_fuzzy}->($urpm, $match, @m) + or return (); + } + } + } + + #- check if something need to be removed. + if ($options{callback_base} && %{$state->{ask_remove} || {}}) { + my @base = qw(basesystem); + my (@base_to_remove, %basepackages, %base); + + #- check if a package to be removed is a part of basesystem requires. + while (defined($_ = shift @base)) { + exists $basepackages{$_} and next; + $db->traverse_tag(/^\// ? 'path' : 'whatprovides', [ $_ ], sub { + my ($p) = @_; + push @{$basepackages{$_} ||= []}, join '-', ($p->fullname)[0..2]; + push @base, $p->requires_nosense; + }); + } + + foreach (values %basepackages) { + my $n = @$_; + foreach (@$_) { + $base{$_} = \$n; + } + } + + foreach (keys %{$state->{ask_remove}}) { + my $rn = $base{$_}; + if ($rn) { + $$rn == 1 and push @base_to_remove, $_; + --$$rn; + } + } + + @base_to_remove and $options{callback_base}->($urpm, @base_to_remove) + || return (); + } + } + + keys %{$state->{ask_remove}}; +} + +#- install packages according to each hashes (install or upgrade). +sub remove { + my ($urpm, $remove, %options) = @_; + my $db = URPM::DB::open($urpm->{root}, !$options{test}); #- open in read/write mode unless testing installation. + my $trans = $db->create_transaction($urpm->{root}); + my @l; + local *F; + + foreach (@$remove) { + $trans->remove($_) or $urpm->{error}(_("unable to remove package %s", $_)); + } + if (!$options{nodeps} and @l = $trans->check) { + if ($options{translate_message}) { + foreach (@l) { + my ($type, $needs, $conflicts) = split '@', $_; + $_ = ($type eq 'requires' ? + _("%s is needed by %s", $needs, $conflicts) : + _("%s conflicts with %s", $needs, $conflicts)); + } + } + return @l; + } + !$options{noorder} and @l = $trans->order and return @l; + + $trans->run($urpm, %options); +} + +#- remove packages from node as remembered according to resolving done. +sub parallel_remove { + my ($urpm, $remove, %options) = @_; + my $state = {}; + my $callback = sub { $urpm->{fatal}(1, "internal distributed urpme fatal error") }; + $urpm->{parallel_handler}->parallel_find_remove($urpm, $state, $remove, %options, + callback_notfound => $callback, + callback_fuzzy => $callback, + callback_base => $callback, + ); +} + 1; diff --git a/urpm/parallel_ka_run.pm b/urpm/parallel_ka_run.pm index 93d90e9d..9aa74497 100644 --- a/urpm/parallel_ka_run.pm +++ b/urpm/parallel_ka_run.pm @@ -15,6 +15,71 @@ sub parallel_register_rpms { } } +#- parallel find_packages_to_remove +sub parallel_find_remove { + my ($parallel, $urpm, $state, $l, %options) = @_; + my ($test, $node, %bad_nodes, %base_to_remove); + local (*F, $_); + + #- keep in mind if the previous selection is still active, it avoid + #- to re-start urpme --test on each node. + if ($options{find_packages_to_remove}) { + delete $state->{ask_remove}; + delete $urpm->{error_remove}; + $test = '--test '; + } else { + @{$urpm->{error_remove} || []} and return @{$urpm->{error_remove}}; + #- no need to restart what has been started before. + $options{test} and return keys %{$state->{ask_remove}}; + $test = ''; + } + + #- now try an iteration of urpmq. + $urpm->{log}("parallel_ka_run: rshp -v $parallel->{options} -- urpme --no-locales --auto $test".(join ' ', map { "'$_'" } @$l)); + open F, "rshp -v $parallel->{options} -- urpme --no-locales --auto $test".join(' ', map { "'$_'" } @$l)." |"; + while (defined ($_ = )) { + chomp; + s/<([^>]*)>.*:->:(.*)/$2/ and $node = $1; + /^\s*$/ and next; + /Checking to remove the following packages/ and next; + /To satisfy dependencies, the following packages are going to be removed/ + and $urpm->{fatal}(1, ("node %s has bad version of urpme, please upgrade", $node)); + if (/unknown packages?:? (.*)/) { + $options{callback_notfound} and $options{callback_notfound}->($urpm, split ", ", $1) + or delete $state->{ask_remove}, last; + } elsif (/The following packages contain ([^:]*): (.*)/) { + $options{callback_fuzzy} and $options{callback_fuzzy}->($urpm, $1, split " ", $2) + or delete $state->{ask_remove}, last; + } elsif (/removing package (.*) will break your system/) { + $base_to_remove{$1} = undef; + } elsif (/Removing failed/) { + $bad_nodes{$node} = []; + } else { + if (exists $bad_nodes{$node}) { + /^\s+(.*)/ and push @{$bad_nodes{$node}}, $1; + } else { + $state->{ask_remove}{$_}{$node} = undef; + } + } + } + close F or $urpm->{fatal}(1, _("rshp failed, maybe a node is unreacheable")); + + #- check base, which has been delayed until there. + $options{callback_base} and %base_to_remove and $options{callback_base}->($urpm, keys %base_to_remove) + || return (); + + #- build error list contains all the error returned by each node. + $urpm->{error_remove} = []; + foreach (keys %bad_nodes) { + my $msg = _("on node %s", $_); + foreach (@{$bad_nodes{$_}}) { + push @{$urpm->{error_remove}}, "$msg, $_"; + } + } + + keys %{$state->{ask_remove}}; +} + #- parallel resolve_dependencies sub parallel_resolve_dependencies { my ($parallel, $synthesis, $urpm, $state, $requested, %options) = @_; @@ -91,7 +156,7 @@ sub parallel_resolve_dependencies { } } } else { - my $pkg = $urpm->search($_) or next; #TODO + my $pkg = $urpm->search($_) or next; $state->{selected}{$pkg->id}{$node} = $_; } } @@ -102,9 +167,6 @@ sub parallel_resolve_dependencies { #- keep trace of what has been chosen finally (if any). $parallel->{line} = "$line ".join(' ', keys %chosen); - - #- update ask_remove, ask_unselect too along with provided value. - #TODO } #- parallel install. diff --git a/urpm/parallel_ssh.pm b/urpm/parallel_ssh.pm index 4d66b372..f5c29405 100644 --- a/urpm/parallel_ssh.pm +++ b/urpm/parallel_ssh.pm @@ -18,6 +18,72 @@ sub parallel_register_rpms { } } +#- parallel find_packages_to_remove +sub parallel_find_remove { + my ($parallel, $urpm, $state, $l, %options) = @_; + my ($test, $node, %bad_nodes, %base_to_remove); + local (*F, $_); + + #- keep in mind if the previous selection is still active, it avoid + #- to re-start urpme --test on each node. + if ($options{find_packages_to_remove}) { + delete $state->{ask_remove}; + delete $urpm->{error_remove}; + $test = '--test '; + } else { + @{$urpm->{error_remove} || []} and return @{$urpm->{error_remove}}; + #- no need to restart what has been started before. + $options{test} and return keys %{$state->{ask_remove}}; + $test = ''; + } + + #- now try an iteration of urpme. + foreach my $node (keys %{$parallel->{nodes}}) { + $urpm->{log}("parallel_ssh: ssh $node urpme --no-locales --auto $test".(join ' ', map { "'$_'" } @$l)); + open F, "ssh 2>&1 $node urpme --no-locales --auto $test".(join ' ', map { "'$_'" } @$l)." |"; + while (defined ($_ = )) { + chomp; + /^\s*$/ and next; + /Checking to remove the following packages/ and next; + /To satisfy dependencies, the following packages are going to be removed/ + and $urpm->{fatal}(1, ("node %s has bad version of urpme, please upgrade", $node)); + if (/unknown packages?:? (.*)/) { + $options{callback_notfound} and $options{callback_notfound}->($urpm, split ", ", $1) + or delete $state->{ask_remove}, last; + } elsif (/The following packages contain ([^:]*): (.*)/) { + $options{callback_fuzzy} and $options{callback_fuzzy}->($urpm, $1, split " ", $2) + or delete $state->{ask_remove}, last; + } elsif (/removing package (.*) will break your system/) { + $base_to_remove{$1} = undef; + } elsif (/Removing failed/) { + $bad_nodes{$node} = []; + } else { + if (exists $bad_nodes{$node}) { + /^\s+(.*)/ and push @{$bad_nodes{$node}}, $1; + } else { + $state->{ask_remove}{$_}{$node} = undef; + } + } + } + close F; + } + + #- check base, which has been delayed until there. + $options{callback_base} and %base_to_remove and $options{callback_base}->($urpm, keys %base_to_remove) + || return (); + + #- build error list contains all the error returned by each node. + $urpm->{error_remove} = []; + foreach (keys %bad_nodes) { + my $msg = _("on node %s", $_); + foreach (@{$bad_nodes{$_}}) { + push @{$urpm->{error_remove}}, "$msg, $_"; + } + } + + keys %{$state->{ask_remove}}; +} + #- parallel resolve_dependencies sub parallel_resolve_dependencies { my ($parallel, $synthesis, $urpm, $state, $requested, %options) = @_; @@ -74,7 +140,7 @@ sub parallel_resolve_dependencies { foreach my $node (keys %{$parallel->{nodes}}) { $urpm->{log}("parallel_ssh: ssh $node urpmq --synthesis $synthesis -fduc $line ".join(' ', keys %chosen)); open F, "ssh $node urpmq --synthesis $synthesis -fduc $line ".join(' ', keys %chosen)." |"; - while ($_ = ) { + while (defined ($_ = )) { chomp; if (/^\@removing\@(.*)/) { $state->{ask_remove}{$1}{$node} = undef; @@ -106,9 +172,6 @@ sub parallel_resolve_dependencies { #- keep trace of what has been chosen finally (if any). $parallel->{line} .= "$line ".join(' ', keys %chosen); - - #- update ask_remove, ask_unselect too along with provided value. - #TODO } #- parallel install. diff --git a/urpme b/urpme index d8244f49..7c593a33 100644 --- a/urpme +++ b/urpme @@ -28,17 +28,14 @@ use urpm; #- get I18N translation method. import urpm _; -my ($auto, $matches, $maymatch, @l, @m, %base); +my (@nextargv, $root, $test, $parallel, $auto, $matches, $maymatch, @l); my $askok = _("Is this OK?"); -my $askrm = _("Remove them all?"); # Translator: Add here the keys which might be pressed in the "No"-case. my $noexpr = _("Nn"); # Translator: Add here the keys which might be pressed in the "Yes"-case. my $yesexpr = _("Yy"); -local $_ = ' ' . join(' ', @ARGV) . ' '; - -if ( / --?h/ || @ARGV == 0 ) { +sub usage { print STDERR _("urpme version %s Copyright (C) 1999, 2000, 2001, 2002 MandrakeSoft. This is free software and may be redistributed under the terms of the GNU GPL. @@ -46,144 +43,61 @@ This is free software and may be redistributed under the terms of the GNU GPL. usage: ", $urpm::VERSION) . _(" --help - print this help message. ") . _(" --auto - automatically select a package in choices. +") . _(" --test - verify if the installation can be achieved correctly. +") . _(" --parallel - distributed urpmi accross machines of alias. ") . _(" -a - select all packages matching expression. "); exit(0); } -$matches = / -a /; -$auto = / --?auto /; - -$urpm = new urpm; -$state = {}; - -#- open database to examine packages... -{ - my $db = URPM::DB::open; - - @l = grep { !/^-/ } @ARGV; - if (!$matches) { - foreach (@l) { - my ($n, $found); - - #- check if name-version-release may have been given. - if (($n) = /^(.*)-[^\-]*-[^\-]*\.[^\.\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - $p->fullname eq $_ or return; - $urpm->resolve_closure_ask_remove($db, $state, $p); - push @m, join('-', ($p->fullname)[0..2]); - $found = 1; - }); - $found and next; - } - - #- check if name-version-release may have been given. - if (($n) = /^(.*)-[^\-]*-[^\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - join('-', ($p->fullname)[0..2]) eq $_ or return; - $urpm->resolve_closure_ask_remove($db, $state, $p); - push @m, join('-', ($p->fullname)[0..2]); - $found = 1; - }); - $found and next; - } - - #- check if name-version may have been given. - if (($n) = /^(.*)-[^\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - join('-', ($p->fullname)[0..1]) eq $_ or return; - $urpm->resolve_closure_ask_remove($db, $state, $p); - push @m, join('-', ($p->fullname)[0..2]); - $found = 1; - }); - $found and next; - } - - #- check if only name may have been given. - $db->traverse_tag('name', [ $_ ], sub { - my ($p) = @_; - $p->name eq $_ or return; - $urpm->resolve_closure_ask_remove($db, $state, $p); - push @m, join('-', ($p->fullname)[0..2]); - $found = 1; - }); - $found and next; - - #- nothing has been found for the given name. - $maymatch .= ($maymatch && ", ") . $_; - } - if ($maymatch) { - my $msg = $maymatch =~ /, / ? _("unknown packages ") : _("unknown package "); - $msg =~ /\s$/ or $msg .= ' '; #- add trailing space to avoid fixing bad translator (too late now for 9.0). - $maymatch = "$msg$maymatch\n"; - } - $maymatch && ($auto || @l > 1) and die $maymatch; - } - if ($matches || $maymatch) { - my $match = join "|", map { quotemeta } @l; - - #- reset what has been already found. - $state = {}; - @m = (); - - #- search for package that matches, and perform closure again. - $db->traverse(sub { - my ($p) = @_; - $p->fullname =~ /$match/ or return; - $urpm->resolve_closure_ask_remove($db, $state, $p); - push @m, join('-', ($p->fullname)[0..2]); - }); - - if ($maymatch) { - @m or die $maymatch; - my $msg = _("Using \"%s\" as a substring, I found", $match); - print STDOUT "$msg:\n@m\n$askrm" . _(" (y/N) "); - =~ /[$yesexpr]/ or exit 1; - } - } - - #- if nothing need to be removed. - unless (%{$state->{ask_remove} || {}}) { - print _("Nothing to remove.\n"); - exit(0); - } - - my @base = qw(basesystem); - my %basepackages; - - #- check if a package to be removed is a part of basesystem requires. - while (defined($_ = shift @base)) { - exists $basepackages{$_} and next; - $db->traverse_tag(/^\// ? 'path' : 'whatprovides', [ $_ ], sub { - my ($p) = @_; - push @{$basepackages{$_} ||= []}, join '-', ($p->fullname)[0..2]; - push @base, $p->requires_nosense; - }); - } - - foreach (values %basepackages) { - my $n = @$_; - foreach (@$_) { - $base{$_} = \$n; - } - } -} - -my $base_str = ''; -my @toremove = keys %{$state->{ask_remove}}; -foreach (@toremove) { - my $rn = $base{$_}; - if ($rn) { - $$rn == 1 and $base_str .= _("removing package %s will break your system\n", $_); - --$$rn; - } +@ARGV or usage; +while (defined($_ = shift @ARGV)) { + /^--help$/ and do { usage; next }; + /^--no-locales$/ and do { undef *_; undef *urpm::_; *_ = *urpm::_ = sub { sprintf(shift @_, @_) }; next }; + /^--?auto$/ and do { $auto = 1; next }; + /^--(no-)?test$/ and do { $test = !$1; next }; + /^--root$/ and do { push @nextargv, \$root; next }; + /^--parallel$/ and do { push @nextargv, \$parallel; next }; + /^-(.*)$/ and do { foreach (split //, $1) { + /[\?h]/ and do { usage; next }; + /a/ and do { $matches = 1; next }; + die _("urpme: unknown option \"-%s\", check usage with --help\n", $1); } next }; + @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next }; + push @l, $_; } -$base_str and die $base_str; -if (@toremove > @l && !$auto) { +my $urpm = new urpm; +my $state = {}; + +#- just configure parallel mode if available. +$parallel and $urpm->configure(synthesis => 'none', + root => $root, + parallel => $parallel, + ); + +#- examine packages... +my @toremove = $urpm->find_packages_to_remove($state, \@l, + test => $test, matches => $matches, auto => $auto, + callback_notfound => sub { + my $urpm = shift @_; + $urpm->{fatal}(1, (@_ > 1 ? _("unknown packages") : _("unknown package")) . + ': ' . join(', ', @_)); 0 }, + callback_fuzzy => sub { + my $urpm = shift @_; + my $match = shift @_; + $urpm->{fatal}(1, _("The following packages contain %s: %s", + $match, join(' ', @_))); 0 }, + callback_base => sub { + my $urpm = shift @_; + foreach (@_) { + $urpm->{error}(_("removing package %s will break your system", $_)); + } 0 }, + ) or $urpm->{fatal}(0, _("Nothing to remove")); + +if ($test && $auto) { + my $msg = _("Checking to remove the following packages"); + print STDOUT "$msg:\n" . join("\n", sort { $a cmp $b } @toremove) . "\n"; +} elsif (@toremove > @l && !$auto) { my $sum = 0; foreach (@toremove) { $sum += $state->{ask_remove}{$_}{size}; @@ -193,11 +107,10 @@ if (@toremove > @l && !$auto) { =~ /[$noexpr]/ and exit 0; } -@l = $urpm->install(\@toremove, {}, {}); -if (@l) { - print STDERR _("Removing failed") . ":\n" . join("\n", map { "\t$_" } @l); - exit 1; -} +@l = $parallel ? + $urpm->parallel_remove(\@toremove, test => $test, translate_message => 1) : + $urpm->remove(\@toremove, test => $test, translate_message => 1); +@l and $urpm->{fatal}(1, _("Removing failed") . ":\n" . join("\n", map { "\t$_" } @l)); sub toMb { my $nb = $_[0] / 1024 / 1024; diff --git a/urpmi.spec b/urpmi.spec index 285cf691..fb6c7cdf 100644 --- a/urpmi.spec +++ b/urpmi.spec @@ -1,15 +1,15 @@ %define group System/Configuration/Packaging Name: urpmi -Version: 4.1 -Release: 18mdk +Version: 4.2 +Release: 1mdk License: GPL Source0: %{name}.tar.bz2 Source1: %{name}.logrotate Summary: User mode rpm install URL: http://cvs.mandrakesoft.com/cgi-bin/cvsweb.cgi/soft/urpmi Requires: eject webfetch perl-DateManip >= 5.40 -PreReq: perl-Locale-gettext rpmtools >= 4.3-6mdk perl-URPM >= 0.80 +PreReq: perl-Locale-gettext rpmtools >= 4.3-6mdk perl-URPM >= 0.81 BuildRequires: bzip2-devel gettext rpm-devel >= 4.0.3 BuildRoot: %{_tmppath}/%{name}-buildroot BuildArch: noarch @@ -37,7 +37,7 @@ gurpmi is a graphical front-end to urpmi %package -n urpmi-parallel-ka-run Summary: Parallel extensions to urpmi using ka-run -Requires: urpmi >= 4.1-8mdk ka-run >= 2.0-15mdk +Requires: urpmi >= 4.2-1mdk ka-run >= 2.0-15mdk Group: %{group} %description -n urpmi-parallel-ka-run urpmi-parallel-ka-run is an extensions module to urpmi for handling @@ -45,7 +45,7 @@ distributed installation using ka-run tools. %package -n urpmi-parallel-ssh Summary: Parallel extensions to urpmi using ssh and scp -Requires: urpmi >= 4.1-8mdk openssh-clients +Requires: urpmi >= 4.2-1mdk openssh-clients Group: %{group} %description -n urpmi-parallel-ssh urpmi-parallel-ssh is an extensions module to urpmi for handling @@ -204,6 +204,12 @@ fi %changelog +* Wed Dec 18 2002 François Pons 4.2-1mdk +- fixed file:// protocol now checking file presence. +- added distributed urpme (both ka-run and ssh module). +- updated perl-URPM and urpmi requires on version (major + fixes in perl-URPM-0.81 and extended urpme in urpmi-4.2). + * Fri Dec 13 2002 François Pons 4.1-18mdk - fixed urpmf so that if callback is not compilable display help. - fixed urpmq and urpmi call without parameter to display help. -- cgit v1.2.1