diff options
Diffstat (limited to 'urpm.pm')
-rw-r--r-- | urpm.pm | 3322 |
1 files changed, 48 insertions, 3274 deletions
@@ -10,17 +10,15 @@ use urpm::download; use urpm::util; use urpm::sys; use urpm::cfg; -use MDV::Distribconf; +use urpm::md5sum; -our $VERSION = '4.8.29'; -our @ISA = qw(URPM); +our $VERSION = '4.9.21'; +our @ISA = qw(URPM Exporter); +our @EXPORT_OK = 'file_from_local_url'; use URPM; use URPM::Resolve; -my $RPMLOCK_FILE; -my $LOCK_FILE; - #- this violently overrides is_arch_compat() to always return true. sub shunt_ignorearch { eval q( sub URPM::Package::is_arch_compat { 1 } ); @@ -35,2107 +33,87 @@ sub new { depslist => [], provides => {}, - config => "/etc/urpmi/urpmi.cfg", - skiplist => "/etc/urpmi/skip.list", - instlist => "/etc/urpmi/inst.list", - statedir => "/var/lib/urpmi", - cachedir => "/var/cache/urpmi", media => undef, options => {}, fatal => sub { printf STDERR "%s\n", $_[1]; exit($_[0]) }, error => sub { printf STDERR "%s\n", $_[0] }, + info => sub { printf "%s\n", $_[0] }, log => sub { printf "%s\n", $_[0] }, ui_msg => sub { $self->{log}($_[0]); ref $self->{ui} && ref $self->{ui}{msg} and $self->{ui}{msg}->($_[1]); }, }, $class; + + set_files($self, ''); $self->set_nofatal(1); $self; } -sub requested_ftp_http_downloader { - my ($urpm, $media_name) = @_; - - $urpm->{options}{downloader} || #- cmd-line switch - $media_name && do { - #- per-media config - my $m = name2medium($urpm, $media_name); - $m && $m->{downloader}; - } || $urpm->{global_config}{downloader}; -} - -#- $medium can be undef -sub sync_webfetch { - my ($urpm, $medium, $files, $std_options, %more_options) = @_; - - my %options = ( - dir => "$urpm->{cachedir}/partial", - limit_rate => $std_options->{limit_rate}, - compress => $std_options->{compress}, - proxy => get_proxy($medium), - quiet => $std_options->{quiet}, #- often overridden in the caller, why?? - $medium ? (media => $medium->{name}) : (), - %more_options, +sub set_files { + my ($urpm, $urpmi_root) = @_; + my %h = ( + config => "$urpmi_root/etc/urpmi/urpmi.cfg", + skiplist => "$urpmi_root/etc/urpmi/skip.list", + instlist => "$urpmi_root/etc/urpmi/inst.list", + private_netrc => "$urpmi_root/etc/urpmi/netrc", + statedir => "$urpmi_root/var/lib/urpmi", + cachedir => "$urpmi_root/var/cache/urpmi", + root => $urpmi_root, + $urpmi_root ? (urpmi_root => $urpmi_root) : (), ); - foreach my $cpt (qw(retry wget-options curl-options rsync-options prozilla-options)) { - $options{$cpt} = $urpm->{options}{$cpt} if defined $urpm->{options}{$cpt}; - } - - _sync_webfetch_raw($urpm, $files, \%options); -} - -#- syncing algorithms. -sub _sync_webfetch_raw { - my ($urpm, $files, $options) = @_; - - my %files; - #- currently ftp and http protocols are managed by curl or wget, - #- ssh and rsync protocols are managed by rsync *AND* ssh. - foreach (@$files) { - my $proto = protocol_from_url($_) or die N("unknown protocol defined for %s", $_); - push @{$files{$proto}}, $_; - } - if ($files{removable} || $files{file}) { - my @l = map { file_from_local_url($_) } @{$files{removable} || []}, @{$files{file} || []}; - eval { sync_file($options, @l) }; - $urpm->{fatal}(10, $@) if $@; - delete @files{qw(removable file)}; - } - if ($files{ftp} || $files{http} || $files{https}) { - my @available = urpm::download::available_ftp_http_downloaders(); - - #- use user default downloader if provided and available - my $requested_downloader = requested_ftp_http_downloader($urpm, $options->{media}); - my ($preferred) = grep { $_ eq $requested_downloader } @available; - if (!$preferred) { - #- else first downloader of @available is the default one - $preferred = $available[0]; - if ($requested_downloader && !our $webfetch_not_available) { - $urpm->{log}(N("%s is not available, falling back on %s", $requested_downloader, $preferred)); - $webfetch_not_available = 1; - } - } - my $sync = $urpm::download::{"sync_$preferred"} or die N("no webfetch found, supported webfetch are: %s\n", join(", ", urpm::download::ftp_http_downloaders())); - $sync->($options, @{$files{ftp} || []}, @{$files{http} || []}, @{$files{https} || []}); - - delete @files{qw(ftp http https)}; - } - if ($files{rsync}) { - sync_rsync($options, @{$files{rsync}}); - delete $files{rsync}; - } - if ($files{ssh}) { - my @ssh_files = map { m!^ssh://([^/]*)(.*)! ? "$1:$2" : () } @{$files{ssh}}; - sync_ssh($options, @ssh_files); - delete $files{ssh}; - } - %files and die N("unable to handle protocol: %s", join ', ', keys %files); -} - -our @PER_MEDIA_OPT = qw( - downloader - hdlist - ignore - key-ids - list - md5sum - noreconfigure - priority - priority-upgrade - removable - static - synthesis - update - verify-rpm - virtual - with_hdlist -); - -#- Loads /etc/urpmi/urpmi.cfg and performs basic checks. -#- Does not handle old format: <name> <url> [with <path_hdlist>] -#- options : -#- - nocheck_access : don't check presence of hdlist and other files -sub read_config { - my ($urpm, %options) = @_; - return if $urpm->{media}; #- media already loaded - $urpm->{media} = []; - my $config = urpm::cfg::load_config($urpm->{config}) - or $urpm->{fatal}(6, $urpm::cfg::err); - - #- global options - if ($config->{''}) { - foreach my $opt (qw( - allow-force - allow-nodeps - auto - compress - downloader - default-media - excludedocs - excludepath - fuzzy - ignoresize - keep - key-ids - limit-rate - nopubkey - norebuild - post-clean - pre-clean - priority-upgrade - prohibit-remove - repackage - resume - retry - split-length - split-level - strict-arch - verify-rpm - curl-options - rsync-options - wget-options - prozilla-options - )) { - if (defined $config->{''}{$opt} && !exists $urpm->{options}{$opt}) { - $urpm->{options}{$opt} = $config->{''}{$opt}; - } - } - } - #- per-media options - foreach my $m (grep { $_ ne '' } keys %$config) { - my $medium = { name => $m, clear_url => $config->{$m}{url} }; - foreach my $opt (@PER_MEDIA_OPT) { - defined $config->{$m}{$opt} and $medium->{$opt} = $config->{$m}{$opt}; - } - $urpm->probe_medium($medium, %options) and push @{$urpm->{media}}, $medium; - } - - eval { require urpm::ldap; urpm::ldap::load_ldap_media($urpm, %options) }; - - #- load default values - foreach (qw(post-clean verify-rpm)) { - exists $urpm->{options}{$_} or $urpm->{options}{$_} = 1; - } - - $urpm->{media} = [ sort { $a->{priority} <=> $b->{priority} } @{$urpm->{media}} ]; - - #- remember if an hdlist or list file is already used - my %filelists; - foreach my $medium (@{$urpm->{media}}) { - foreach my $filetype (qw(hdlist list)) { - $medium->{$filetype} or next; - - if ($filelists{$filetype}{$medium->{$filetype}}) { - $medium->{ignore} = 1; - $urpm->{error}( - $filetype eq 'hdlist' - ? N("medium \"%s\" trying to use an already used hdlist, medium ignored", $medium->{name}) - : N("medium \"%s\" trying to use an already used list, medium ignored", $medium->{name}) - ); - } else { - $filelists{$filetype}{$medium->{$filetype}} = 1; - } - } - } - - #- check the presence of hdlist and list files if necessary. - if (!$options{nocheck_access}) { - foreach my $medium (@{$urpm->{media}}) { - $medium->{ignore} and next; - - if (-r statedir_hdlist($urpm, $medium)) {} - elsif ($medium->{synthesis} && -r statedir_synthesis($urpm, $medium)) {} - else { - $medium->{ignore} = 1; - $urpm->{error}(N("unable to access hdlist file of \"%s\", medium ignored", $medium->{name})); - } - if ($medium->{list} && -r statedir_list($urpm, $medium)) {} - elsif ($medium->{url}) {} - else { - $medium->{ignore} = 1; - $urpm->{error}(N("unable to access list file of \"%s\", medium ignored", $medium->{name})); - } - } - } - - #- read MD5 sums (usually not in urpmi.cfg but in a separate file) - foreach (@{$urpm->{media}}) { - if (my $md5sum = get_md5sum("$urpm->{statedir}/MD5SUM", statedir_hdlist_or_synthesis($urpm, $_))) { - $_->{md5sum} = $md5sum; - } - } - - #- remember global options for write_config - $urpm->{global_config} = $config->{''}; -} - -#- probe medium to be used, take old medium into account too. -sub probe_medium { - my ($urpm, $medium, %options) = @_; - local $_; - - if (name2medium($urpm, $medium->{name})) { - $urpm->{error}(N("trying to override existing medium \"%s\", skipping", $medium->{name})); - return; - } + $urpm->{$_} = $h{$_} foreach keys %h; - $medium->{url} ||= $medium->{clear_url}; - - if ($medium->{virtual}) { - #- a virtual medium needs to have an url available without using a list file. - if ($medium->{hdlist} || $medium->{list}) { - $medium->{ignore} = 1; - $urpm->{error}(N("virtual medium \"%s\" should not have defined hdlist or list file, medium ignored", - $medium->{name})); - } - unless ($medium->{url}) { - $medium->{ignore} = 1; - $urpm->{error}(N("virtual medium \"%s\" should have a clear url, medium ignored", - $medium->{name})); - } - } else { - unless ($medium->{ignore} || $medium->{hdlist}) { - $medium->{hdlist} = "hdlist.$medium->{name}.cz"; - -e statedir_hdlist($urpm, $medium) or - $medium->{ignore} = 1, - $urpm->{error}(N("unable to find hdlist file for \"%s\", medium ignored", $medium->{name})); - } - unless ($medium->{ignore} || $medium->{list}) { - unless (defined $medium->{url}) { - $medium->{list} = "list.$medium->{name}"; - unless (-e statedir_list($urpm, $medium)) { - $medium->{ignore} = 1, - $urpm->{error}(N("unable to find list file for \"%s\", medium ignored", $medium->{name})); - } - } - } - - #- there is a little more to do at this point as url is not known, inspect directly list file for it. - unless ($medium->{url}) { - my %probe; - if (-r statedir_list($urpm, $medium)) { - my $listfile = $urpm->open_safe("<", statedir_list($urpm, $medium)); - if ($listfile) { - while (<$listfile>) { - #- /./ is end of url marker in list file (typically generated by a - #- find . -name "*.rpm" > list - #- for exportable list file. - m|^(.*)/\./| and $probe{$1} = undef; - m|^(.*)/[^/]*$| and $probe{$1} = undef; - } - close $listfile; - } - } - foreach (sort { length($a) <=> length($b) } keys %probe) { - if ($medium->{url}) { - if ($medium->{url} ne substr($_, 0, length($medium->{url}))) { - $medium->{ignore} or $urpm->{error}(N("inconsistent list file for \"%s\", medium ignored", $medium->{name})); - $medium->{ignore} = 1; - last; - } - } else { - $medium->{url} = $_; - } - } - unless ($options{nocheck_access}) { - unless ($medium->{url}) { - $medium->{ignore} or $urpm->{error}(N("unable to inspect list file for \"%s\", medium ignored", $medium->{name})); - $medium->{ignore} = 1; - } - } - } - } - - #- probe removable device. - $urpm->probe_removable_device($medium); - - #- clear URLs for trailing /es. - $medium->{url} and $medium->{url} =~ s|(.*?)/*$|$1|; - $medium->{clear_url} and $medium->{clear_url} =~ s|(.*?)/*$|$1|; - - $medium; -} - -#- returns the removable device name if it corresponds to an iso image, '' otherwise -sub is_iso { - my ($removable_dev) = @_; - $removable_dev && $removable_dev =~ /\.iso$/i; + require File::Path; + File::Path::mkpath([ $h{statedir}, + (map { "$h{cachedir}/$_" } qw(headers partial rpms)), + dirname($h{config}), + "$urpmi_root/var/lib/rpm", + ]); } sub protocol_from_url { my ($url) = @_; - $url =~ m!^([^:_]*)[^:]*:! && $1; + $url =~ m!^(\w+)(_[^:]*)?:! && $1; } sub file_from_local_url { my ($url) = @_; $url =~ m!^(?:removable[^:]*:/|file:/)?(/.*)! && $1; } -sub file_from_file_url { - my ($url) = @_; - $url =~ m!^(?:file:/)?(/.*)! && $1; -} - -sub hdlist_or_synthesis_for_virtual_medium { - my ($medium) = @_; - - my $path = file_from_file_url($medium->{url}) or return; - "$path/$medium->{with_hdlist}"; -} - -sub statedir_hdlist_or_synthesis { - my ($urpm, $medium) = @_; - $medium->{hdlist} && "$urpm->{statedir}/" . ($medium->{synthesis} ? 'synthesis.' : '') . $medium->{hdlist}; -} -sub statedir_hdlist { - my ($urpm, $medium) = @_; - $medium->{hdlist} && "$urpm->{statedir}/$medium->{hdlist}"; -} -sub statedir_synthesis { - my ($urpm, $medium) = @_; - $medium->{hdlist} && "$urpm->{statedir}/synthesis.$medium->{hdlist}"; -} -sub statedir_list { - my ($urpm, $medium) = @_; - $medium->{list} && "$urpm->{statedir}/$medium->{list}"; -} -sub statedir_descriptions { - my ($urpm, $medium) = @_; - $medium->{name} && "$urpm->{statedir}/descriptions.$medium->{name}"; -} -sub statedir_names { - my ($urpm, $medium) = @_; - $medium->{name} && "$urpm->{statedir}/names.$medium->{name}"; -} -sub cachedir_hdlist { - my ($urpm, $medium) = @_; - $medium->{hdlist} && "$urpm->{cachedir}/partial/$medium->{hdlist}"; -} -sub cachedir_list { - my ($urpm, $medium) = @_; - $medium->{list} && "$urpm->{cachedir}/partial/$medium->{list}"; -} - -sub name2medium { - my ($urpm, $name) = @_; - my ($medium) = grep { $_->{name} eq $name } @{$urpm->{media}}; - $medium; -} - -#- probe device associated with a removable device. -sub probe_removable_device { - my ($urpm, $medium) = @_; - - if ($medium->{url} && $medium->{url} =~ /^removable/) { - #- try to find device name in url scheme, this is deprecated, use medium option "removable" instead - if ($medium->{url} =~ /^removable_?([^_:]*)/) { - $medium->{removable} ||= $1 && "/dev/$1"; - } - } else { - delete $medium->{removable}; - return; - } - - #- try to find device to open/close for removable medium. - if (my $dir = file_from_local_url($medium->{url})) { - my %infos; - my @mntpoints = urpm::sys::find_mntpoints($dir, \%infos); - if (@mntpoints > 1) { #- return value is suitable for an hash. - $urpm->{log}(N("too many mount points for removable medium \"%s\"", $medium->{name})); - $urpm->{log}(N("taking removable device as \"%s\"", join ',', map { $infos{$_}{device} } @mntpoints)); - } - if (is_iso($medium->{removable})) { - $urpm->{log}(N("Medium \"%s\" is an ISO image, will be mounted on-the-fly", $medium->{name})); - } elsif (@mntpoints) { - if ($medium->{removable} && $medium->{removable} ne $infos{$mntpoints[-1]}{device}) { - $urpm->{log}(N("using different removable device [%s] for \"%s\"", - $infos{$mntpoints[-1]}{device}, $medium->{name})); - } - $medium->{removable} = $infos{$mntpoints[-1]}{device}; - } else { - $urpm->{error}(N("unable to retrieve pathname for removable medium \"%s\"", $medium->{name})); - } - } else { - $urpm->{error}(N("unable to retrieve pathname for removable medium \"%s\"", $medium->{name})); - } -} - - -sub write_MD5SUM { - my ($urpm) = @_; - - #- write MD5SUM file - my $fh = $urpm->open_safe('>', "$urpm->{statedir}/MD5SUM") or return 0; - foreach my $medium (grep { $_->{md5sum} } @{$urpm->{media}}) { - my $s = basename(statedir_hdlist_or_synthesis($urpm, $medium)); - print $fh "$medium->{md5sum} $s\n"; - } - - $urpm->{log}(N("wrote %s", "$urpm->{statedir}/MD5SUM")); - - delete $urpm->{md5sum_modified}; -} - -#- Writes the urpmi.cfg file. -sub write_urpmi_cfg { - my ($urpm) = @_; - - #- avoid trashing exiting configuration if it wasn't loaded - $urpm->{media} or return; - - my $config = { - #- global config options found in the config file, without the ones - #- set from the command-line - '' => $urpm->{global_config}, - }; - foreach my $medium (@{$urpm->{media}}) { - next if $medium->{external}; - my $medium_name = $medium->{name}; - $config->{$medium_name}{url} = $medium->{clear_url}; - foreach (@PER_MEDIA_OPT) { - defined $medium->{$_} and $config->{$medium_name}{$_} = $medium->{$_}; - } - } - urpm::cfg::dump_config($urpm->{config}, $config) - or $urpm->{fatal}(6, N("unable to write config file [%s]", $urpm->{config})); - - $urpm->{log}(N("wrote config file [%s]", $urpm->{config})); - - #- everything should be synced now. - delete $urpm->{modified}; -} - -sub write_config { - my ($urpm) = @_; - - write_urpmi_cfg($urpm); - write_MD5SUM($urpm); -} - -sub _configure_parallel { - my ($urpm, $alias) = @_; - my @parallel_options; - #- read parallel configuration - foreach (cat_("/etc/urpmi/parallel.cfg")) { - chomp; s/#.*$//; s/^\s*//; s/\s*$//; - /\s*([^:]*):(.*)/ or $urpm->{error}(N("unable to parse \"%s\" in file [%s]", $_, "/etc/urpmi/parallel.cfg")), next; - $1 eq $alias and push @parallel_options, $2; - } - #- if a configuration option has been found, use it; else fatal error. - my $parallel_handler; - if (@parallel_options) { - foreach my $dir (grep { -d $_ } map { "$_/urpm" } @INC) { - foreach my $pm (grep { -f $_ } glob("$dir/parallel*.pm")) { - #- load parallel modules - $urpm->{log}->(N("examining parallel handler in file [%s]", $pm)); - # perl_checker: require urpm::parallel_ka_run - # perl_checker: require urpm::parallel_ssh - eval { require $pm; $parallel_handler = $urpm->handle_parallel_options(join("\n", @parallel_options)) }; - $parallel_handler and last; - } - $parallel_handler and last; - } - } - if ($parallel_handler) { - if ($parallel_handler->{nodes}) { - $urpm->{log}->(N("found parallel handler for nodes: %s", join(', ', keys %{$parallel_handler->{nodes}}))); - } - $urpm->{parallel_handler} = $parallel_handler; - } else { - $urpm->{fatal}(1, N("unable to use parallel option \"%s\"", $alias)); - } -} - -#- read urpmi.cfg file as well as necessary synthesis files -#- options : -#- callback -#- call_back_only_once -#- excludemedia -#- hdlist -#- media -#- nodepslist -#- noinstalling -#- noskipping -#- parallel -#- root -#- searchmedia -#- skip -#- sortmedia -#- update -#- usedistrib -sub configure { - my ($urpm, %options) = @_; - - $urpm->clean; - - $options{parallel} && $options{usedistrib} and $urpm->{fatal}(1, N("Can't use parallel mode with use-distrib mode")); - - if ($options{parallel}) { - _configure_parallel($urpm, $options{parallel}); - - if (!$options{media} && $urpm->{parallel_handler}{media}) { - $options{media} = $urpm->{parallel_handler}{media}; - $urpm->{log}->(N("using associated media for parallel mode: %s", $options{media})); - } - } else { - #- parallel is exclusive against root options. - $urpm->{root} = $options{root}; - } - - $urpm->{root} && ! -c "$urpm->{root}/dev/null" - and $urpm->{error}(N("there doesn't seem to be devices in the chroot in \"%s\"", $urpm->{root})); - - if ($options{synthesis}) { - if ($options{synthesis} ne 'none') { - #- synthesis take precedence over media, update options. - $options{media} || $options{excludemedia} || $options{sortmedia} || $options{update} || $options{parallel} and - $urpm->{fatal}(1, N("--synthesis cannot be used with --media, --excludemedia, --sortmedia, --update or --parallel")); - $urpm->parse_synthesis($options{synthesis}); - #- synthesis disables the split of transaction (too risky and not useful). - $urpm->{options}{'split-length'} = 0; - } - } else { - if ($options{usedistrib}) { - $urpm->{media} = []; - $urpm->add_distrib_media("Virtual", $options{usedistrib}, %options, 'virtual' => 1); - } else { - $urpm->read_config(%options); - if (!$options{media} && $urpm->{options}{'default-media'}) { - $options{media} = $urpm->{options}{'default-media'}; - } - } - if ($options{media}) { - delete $_->{modified} foreach @{$urpm->{media} || []}; - $urpm->select_media(split /,/, $options{media}); - foreach (grep { !$_->{modified} } @{$urpm->{media} || []}) { - #- this is only a local ignore that will not be saved. - $_->{tempignore} = $_->{ignore} = 1; - } - } - if ($options{searchmedia}) { - $urpm->select_media($options{searchmedia}); #- Ensure this media has been selected - foreach (grep { !$_->{ignore} } @{$urpm->{media} || []}) { - $_->{name} eq $options{searchmedia} and do { - $_->{searchmedia} = 1; - last; - }; - } - } - if ($options{excludemedia}) { - delete $_->{modified} foreach @{$urpm->{media} || []}; - foreach (select_media_by_name($urpm, [ split /,/, $options{excludemedia} ], {})) { - $_->{modified} = 1; - #- this is only a local ignore that will not be saved. - $_->{tempignore} = $_->{ignore} = 1; - } - } - if ($options{sortmedia}) { - my @sorted_media = map { select_media_by_name($urpm, [$_], {}) } split(/,/, $options{sortmedia}); - my @remaining = difference2($urpm->{media}, \@sorted_media); - $urpm->{media} = [ @sorted_media, @remaining ]; - } - unless ($options{nodepslist}) { - my $second_pass; - do { - foreach (grep { !$_->{ignore} && (!$options{update} || $_->{update}) } @{$urpm->{media} || []}) { - our $currentmedia = $_; #- hack for urpmf - delete @$_{qw(start end)}; - if ($_->{virtual}) { - if (file_from_file_url($_->{url})) { - if ($_->{synthesis}) { - _parse_synthesis($urpm, $_, - hdlist_or_synthesis_for_virtual_medium($_), callback => $options{callback}); - } else { - #- we'll need a second pass - defined $second_pass or $second_pass = 1; - _parse_hdlist($urpm, $_, - hdlist_or_synthesis_for_virtual_medium($_), - callback => $options{call_back_only_once} && $second_pass ? undef : $options{callback}, - ); - } - } else { - $urpm->{error}(N("virtual medium \"%s\" is not local, medium ignored", $_->{name})); - $_->{ignore} = 1; - } - } else { - if ($options{hdlist} && file_size(statedir_hdlist($urpm, $_)) > 32) { - _parse_hdlist($urpm, $_, statedir_hdlist($urpm, $_), - callback => $options{callback}, - ); - } else { - _parse_synthesis($urpm, $_, - statedir_synthesis($urpm, $_), - callback => $options{callback}, - ); - if (!is_valid_medium($_)) { - _parse_hdlist($urpm, $_, statedir_hdlist($urpm, $_), - callback => $options{callback}, - ); - } - } - } - unless ($_->{ignore}) { - _check_after_reading_hdlist_or_synthesis($urpm, $_); - } - unless ($_->{ignore}) { - if ($_->{searchmedia}) { - ($urpm->{searchmedia}{start}, $urpm->{searchmedia}{end}) = ($_->{start}, $_->{end}); - $urpm->{log}(N("Search start: %s end: %s", - $urpm->{searchmedia}{start}, $urpm->{searchmedia}{end})); - delete $_->{searchmedia}; - } - } - } - } while $second_pass && do { - require URPM::Build; - $urpm->{log}(N("performing second pass to compute dependencies\n")); - $urpm->unresolved_provides_clean; - $second_pass--; - }; - } - } - #- determine package to withdraw (from skip.list file) only if something should be withdrawn. - unless ($options{noskipping}) { - my %uniq; - $urpm->compute_flags( - get_packages_list($urpm->{skiplist}, $options{skip}), - skip => 1, - callback => sub { - my ($urpm, $pkg) = @_; - $pkg->is_arch_compat && ! exists $uniq{$pkg->fullname} or return; - $uniq{$pkg->fullname} = undef; - $urpm->{log}(N("skipping package %s", scalar($pkg->fullname))); - }, - ); - } - unless ($options{noinstalling}) { - my %uniq; - $urpm->compute_flags( - get_packages_list($urpm->{instlist}), - disable_obsolete => 1, - callback => sub { - my ($urpm, $pkg) = @_; - $pkg->is_arch_compat && ! exists $uniq{$pkg->fullname} or return; - $uniq{$pkg->fullname} = undef; - $urpm->{log}(N("would install instead of upgrade package %s", scalar($pkg->fullname))); - }, - ); - } - if ($options{bug}) { - #- and a dump of rpmdb itself as synthesis file. - my $db = db_open_or_die($urpm, $options{root}); - my $sig_handler = sub { undef $db; exit 3 }; - local $SIG{INT} = $sig_handler; - local $SIG{QUIT} = $sig_handler; - - open my $rpmdb, "| " . ($ENV{LD_LOADER} || '') . " gzip -9 >'$options{bug}/rpmdb.cz'" - or $urpm->syserror("Can't fork", "gzip"); - $db->traverse(sub { - my ($p) = @_; - #- this is not right but may be enough. - my $files = join '@', grep { exists($urpm->{provides}{$_}) } $p->files; - $p->pack_header; - $p->build_info(fileno $rpmdb, $files); - }); - close $rpmdb; - } -} - -#- add a new medium, sync the config file accordingly. -#- returns the new medium's name. (might be different from the requested -#- name if index_name was specified) -sub add_medium { - my ($urpm, $name, $url, $with_hdlist, %options) = @_; - - #- make sure configuration has been read. - $urpm->{media} or die "caller should have used ->read_config or ->configure first"; - $urpm->lock_urpmi_db('exclusive') if !$options{nolock}; - - #- if a medium with that name has already been found, we have to exit now - my $medium; - if (defined $options{index_name}) { - my $i = $options{index_name}; - do { - ++$i; - $medium = name2medium($urpm, $name . $i); - } while $medium; - $name .= $i; - } else { - $medium = name2medium($urpm, $name); - } - $medium and $urpm->{fatal}(5, N("medium \"%s\" already exists", $medium->{name})); - - $url =~ s,/*$,,; #- clear URLs for trailing /es. - - #- creating the medium info. - $medium = { name => $name, url => $url, update => $options{update}, modified => 1, ignore => $options{ignore} }; - if ($options{virtual}) { - file_from_file_url($url) or $urpm->{fatal}(1, N("virtual medium needs to be local")); - $medium->{virtual} = 1; - } else { - $medium->{hdlist} = "hdlist.$name.cz"; - $medium->{list} = "list.$name"; - $urpm->probe_removable_device($medium); - } - - #- local media have priority, other are added at the end. - if (file_from_file_url($url)) { - $medium->{priority} = 0.5; - } else { - $medium->{priority} = 1 + @{$urpm->{media}}; - } - - #- check whether a password is visible, if not, set clear_url. - my $has_password = $url =~ m|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|; - $medium->{clear_url} = $url unless $has_password; - - $with_hdlist and $medium->{with_hdlist} = $with_hdlist; - - #- create an entry in media list. - push @{$urpm->{media}}, $medium; - - $urpm->{log}(N("added medium %s", $name)); - - #- we need to reload the config, since some string substitutions may have occured - unless ($options{no_reload_config}) { - $urpm->write_config; - delete $urpm->{media}; - $urpm->read_config(nocheck_access => 1); - - #- need getting the fresh datastructure after read_config - $medium = name2medium($urpm, $name); #- need getting the fresh datastructure after read_config - - #- Remember that the database has been modified and base files need to be updated. - $medium->{modified} = 1; - $urpm->{md5sum_modified} = 1; - } - if ($has_password) { - $medium->{url} = $url; - } - - $options{nolock} or $urpm->unlock_urpmi_db; - $name; -} - -#- add distribution media, according to url given. -#- returns the list of names of added media. -#- options : -#- - limit_rate, compress : for downloading files -#- - initial_number : when adding several numbered media, start with this number -#- - probe_with : if eq 'synthesis', use synthesis instead of hdlists -#- - ask_media : callback to know whether each media should be added -#- other options are passed to add_medium() -sub add_distrib_media { - my ($urpm, $name, $url, %options) = @_; - - #- make sure configuration has been read. - $urpm->{media} or die "caller should have used ->read_config or ->configure first"; - - my $distribconf; - - if (my $dir = file_from_local_url($url)) { - $urpm->try_mounting($dir) - or $urpm->{error}(N("unable to mount the distribution medium")), return (); - $distribconf = MDV::Distribconf->new($dir, undef); - $distribconf->load - or $urpm->{error}(N("this location doesn't seem to contain any distribution")), return (); - } else { - unlink "$urpm->{cachedir}/partial/media.cfg"; - - $distribconf = MDV::Distribconf->new($url, undef); - $distribconf->settree('mandriva'); - - eval { - $urpm->{log}(N("retrieving media.cfg file...")); - sync_webfetch($urpm, undef, - [ reduce_pathname($distribconf->getfullpath(undef, 'infodir') . '/media.cfg') ], - \%options, quiet => 1); - $urpm->{log}(N("...retrieving done")); - }; - $@ and $urpm->{error}(N("...retrieving failed: %s", $@)); - if (-e "$urpm->{cachedir}/partial/media.cfg") { - $distribconf->parse_mediacfg("$urpm->{cachedir}/partial/media.cfg") - or $urpm->{error}(N("unable to parse media.cfg")), return(); - } else { - $urpm->{error}(N("unable to access the distribution medium (no media.cfg file found)")); - return (); - } - } - - #- cosmetic update of name if it contains spaces. - $name =~ /\s/ and $name .= ' '; - - my @newnames; - #- at this point, we have found a media.cfg file, so parse it - #- and create all necessary media according to it. - my $medium = $options{initial_number} || 1; - - foreach my $media ($distribconf->listmedia) { - my $skip = 0; - # if one of those values is set, by default, we skip adding the media - foreach (qw(noauto)) { - $distribconf->getvalue($media, $_) and do { - $skip = 1; - last; - }; - } - if ($options{ask_media}) { - if ($options{ask_media}->( - $distribconf->getvalue($media, 'name'), - !$skip, - )) { - $skip = 0; - } else { - $skip = 1; - } - } - $skip and next; - - my $media_name = $distribconf->getvalue($media, 'name') || ''; - my $is_update_media = $distribconf->getvalue($media, 'updates_for'); - - push @newnames, $urpm->add_medium( - $name ? "$media_name ($name$medium)" : $media_name, - reduce_pathname($distribconf->getfullpath($media, 'path')), - offset_pathname( - $url, - $distribconf->getpath($media, 'path'), - ) . '/' . $distribconf->getpath($media, $options{probe_with} eq 'synthesis' ? 'synthesis' : 'hdlist'), - index_name => $name ? undef : 0, - no_reload_config => 1, #- no need to reload config each time, since we don't update the media - %options, - # the following override %options - update => $is_update_media ? 1 : undef, - ); - ++$medium; - } - return @newnames; -} - -#- deprecated, use select_media_by_name instead -sub select_media { - my $urpm = shift; - my $options = {}; - if (ref $_[0]) { $options = shift } - foreach (select_media_by_name($urpm, [ @_ ], $options)) { - #- select medium by setting the modified flag, do not check ignore. - $_->{modified} = 1; - } -} - -sub select_media_by_name { - my ($urpm, $names, $options) = @_; - - my %wanted = map { $_ => 1 } @$names; - - #- first the exact matches - my @l = grep { delete $wanted{$_->{name}} } @{$urpm->{media}}; - - #- check if some arguments don't correspond to the medium name. - #- in such case, try to find the unique medium (or list candidate - #- media found). - foreach (keys %wanted) { - my $q = quotemeta; - my (@found, @foundi); - my $regex = $options->{strict_match} ? qr/^$q$/ : qr/$q/; - my $regexi = $options->{strict_match} ? qr/^$q$/i : qr/$q/i; - foreach my $medium (@{$urpm->{media}}) { - $medium->{name} =~ $regex and push @found, $medium; - $medium->{name} =~ $regexi and push @foundi, $medium; - } - @found = @foundi if !@found; - - if (@found == 0) { - $urpm->{error}(N("trying to select nonexistent medium \"%s\"", $_)); - } else { - if (@found > 1) { - $urpm->{log}(N("selecting multiple media: %s", join(", ", map { qq("$_->{name}") } @found))); - } - #- changed behaviour to select all occurences by default. - push @l, @found; - } - } - @l; -} - -#- deprecated, use remove_media instead -sub remove_selected_media { - my ($urpm) = @_; - - remove_media($urpm, [ grep { $_->{modified} } @{$urpm->{media}} ]); -} - -sub remove_media { - my ($urpm, $to_remove) = @_; - - foreach my $medium (@$to_remove) { - $urpm->{log}(N("removing medium \"%s\"", $medium->{name})); - - #- mark to re-write configuration. - $urpm->{modified} = 1; - - #- remove files associated with this medium. - unlink grep { $_ } map { $_->($urpm, $medium) } \&statedir_hdlist, \&statedir_list, \&statedir_synthesis, \&statedir_descriptions, \&statedir_names; - - #- remove proxy settings for this media - urpm::download::remove_proxy_media($medium->{name}); - } - - $urpm->{media} = [ difference2($urpm->{media}, $to_remove) ]; -} - -#- return list of synthesis or hdlist reference to probe. -sub _probe_with_try_list { - my ($suffix, $probe_with) = @_; - my @probe = ( - "media_info/synthesis.hdlist.cz", - "../media_info/synthesis.hdlist_$suffix.cz", - "synthesis.hdlist.cz", - ); - my @probe_hdlist = ( - "media_info/hdlist.cz", - "../media_info/hdlist_$suffix.cz", - "hdlist.cz", - ); - if ($probe_with =~ /synthesis/) { - push @probe, @probe_hdlist; - } else { - unshift @probe, @probe_hdlist; - } - @probe; -} - -sub may_reconfig_urpmi { - my ($urpm, $medium, $options) = @_; - - my $f; - if (my $dir = file_from_file_url($medium->{url})) { - $f = reduce_pathname("$dir/reconfig.urpmi"); - } else { - unlink($f = "$urpm->{cachedir}/partial/reconfig.urpmi"); - eval { - sync_webfetch($urpm, $medium, [ reduce_pathname("$medium->{url}/reconfig.urpmi") ], - $options, quiet => 1); - }; - } - if (-s $f) { - reconfig_urpmi($urpm, $f, $medium->{name}); - } - unlink $f if !file_from_file_url($medium->{url}); -} - -#- read a reconfiguration file for urpmi, and reconfigure media accordingly -#- $rfile is the reconfiguration file (local), $name is the media name -#- -#- the format is similar to the RewriteRule of mod_rewrite, so: -#- PATTERN REPLACEMENT [FLAG] -#- where FLAG can be L or N -#- -#- example of reconfig.urpmi: -#- # this is an urpmi reconfiguration file -#- /cooker /cooker/$ARCH -sub reconfig_urpmi { - my ($urpm, $rfile, $name) = @_; - -r $rfile or return; - - $urpm->{log}(N("reconfiguring urpmi for media \"%s\"", $name)); - - my ($magic, @lines) = cat_($rfile); - #- the first line of reconfig.urpmi must be magic, to be sure it's not an error file - $magic =~ /^# this is an urpmi reconfiguration file/ or return undef; - - my @replacements; - foreach (@lines) { - chomp; - s/^\s*//; s/#.*$//; s/\s*$//; - $_ or next; - my ($p, $r, $f) = split /\s+/, $_, 3; - push @replacements, [ quotemeta $p, $r, $f || 1 ]; - } - - my $reconfigured = 0; - my @reconfigurable = qw(url with_hdlist clear_url); - - my $medium = name2medium($urpm, $name) or return; - my %orig = %$medium; - - URLS: - foreach my $k (@reconfigurable) { - foreach my $r (@replacements) { - if ($medium->{$k} =~ s/$r->[0]/$r->[1]/) { - $reconfigured = 1; - #- Flags stolen from mod_rewrite: L(ast), N(ext) - if ($r->[2] =~ /L/) { - last; - } elsif ($r->[2] =~ /N/) { #- dangerous option - redo URLS; - } - } - } - #- check that the new url exists before committing changes (local mirrors) - my $file = file_from_local_url($medium->{$k}); - if ($file && !-e $file) { - %$medium = %orig; - $reconfigured = 0; - $urpm->{log}(N("...reconfiguration failed")); - return; - } - } - - if ($reconfigured) { - $urpm->{log}(N("reconfiguration done")); - $urpm->write_config; - } - $reconfigured; -} - -sub _guess_hdlist_suffix { - my ($dir) = @_; - my ($suffix) = $dir =~ m!\bmedia/(\w+)/*\Z!; - $suffix; -} - -sub _hdlist_suffix { - my ($medium) = @_; - $medium->{with_hdlist} =~ /hdlist(.*?)(?:\.src)?\.cz$/ ? $1 : ''; -} - -sub _update_media__when_not_modified { - my ($urpm, $medium) = @_; - - delete @$medium{qw(start end)}; - if ($medium->{virtual}) { - if (file_from_file_url($medium->{url})) { - if ($medium->{synthesis}) { - _parse_synthesis($urpm, $medium, hdlist_or_synthesis_for_virtual_medium($medium)); - } else { - _parse_hdlist($urpm, $medium, hdlist_or_synthesis_for_virtual_medium($medium)); - } - } else { - $urpm->{error}(N("virtual medium \"%s\" is not local, medium ignored", $medium->{name})); - $medium->{ignore} = 1; - } - } else { - _parse_synthesis($urpm, $medium, statedir_synthesis($urpm, $medium)); - if (!is_valid_medium($medium)) { - _parse_hdlist($urpm, $medium, statedir_hdlist($urpm, $medium)); - } - } - unless ($medium->{ignore}) { - _check_after_reading_hdlist_or_synthesis($urpm, $medium); - } -} - -sub _update_media__virtual { - my ($urpm, $medium, $with_hdlist_dir) = @_; - - if ($medium->{with_hdlist} && -e $with_hdlist_dir) { - delete @$medium{qw(start end)}; - if ($medium->{synthesis}) { - _parse_synthesis($urpm, $medium, $with_hdlist_dir); - delete $medium->{modified}; - $medium->{synthesis} = 1; - $urpm->{md5sum_modified} = 1; - if (!is_valid_medium($medium)) { - _parse_hdlist($urpm, $medium, $with_hdlist_dir); - delete @$medium{qw(modified synthesis)}; - } - } else { - _parse_hdlist($urpm, $medium, $with_hdlist_dir); - delete @$medium{qw(modified synthesis)}; - $urpm->{md5sum_modified} = 1; - if (!is_valid_medium($medium)) { - _parse_synthesis($urpm, $medium, $with_hdlist_dir); - delete $medium->{modified}; - $medium->{synthesis} = 1; - } - } - _check_after_reading_hdlist_or_synthesis($urpm, $medium); - } else { - $urpm->{error}(N("virtual medium \"%s\" should have valid source hdlist or synthesis, medium ignored", - $medium->{name})); - $medium->{ignore} = 1; - } -} - -sub generate_media_names { - my ($urpm) = @_; - - #- make sure names files are regenerated. - foreach (@{$urpm->{media}}) { - unlink statedir_names($urpm, $_); - if (is_valid_medium($_)) { - my $fh = $urpm->open_safe(">", statedir_names($urpm, $_)); - if ($fh) { - foreach ($_->{start} .. $_->{end}) { - if (defined $urpm->{depslist}[$_]) { - print $fh $urpm->{depslist}[$_]->name . "\n"; - } else { - $urpm->{error}(N("Error generating names file: dependency %d not found", $_)); - } - } - close $fh; - } else { - $urpm->{error}(N("Error generating names file: Can't write to file (%s)", $!)); - } - } - } -} - - -sub _read_existing_synthesis_and_hdlist_if_same_time_and_msize { - my ($urpm, $medium, $basename) = @_; - - same_size_and_mtime("$urpm->{cachedir}/partial/$basename", - statedir_hdlist($urpm, $medium)) or return; - - unlink "$urpm->{cachedir}/partial/$basename"; - - _read_existing_synthesis_and_hdlist($urpm, $medium); - - 1; -} - -sub _read_existing_synthesis_and_hdlist_if_same_md5sum { - my ($urpm, $medium, $retrieved_md5sum) = @_; - - #- if an existing hdlist or synthesis file has the same md5sum, we assume the - #- files are the same. - #- if local md5sum is the same as distant md5sum, this means there is no need to - #- download hdlist or synthesis file again. - $retrieved_md5sum && $medium->{md5sum} eq $retrieved_md5sum or return; - - unlink "$urpm->{cachedir}/partial/" . basename($medium->{with_hdlist}); - - _read_existing_synthesis_and_hdlist($urpm, $medium); - - 1; -} - -sub _read_existing_synthesis_and_hdlist { - my ($urpm, $medium) = @_; - - $urpm->{log}(N("medium \"%s\" is up-to-date", $medium->{name})); - - #- the medium is now considered not modified. - $medium->{modified} = 0; - #- XXX we could link the new hdlist to the old one. - #- (However links need to be managed. see bug #12391.) - #- as previously done, just read synthesis file here, this is enough. - _parse_synthesis($urpm, $medium, statedir_synthesis($urpm, $medium)); - if (!is_valid_medium($medium)) { - _parse_hdlist($urpm, $medium, statedir_hdlist($urpm, $medium)); - _check_after_reading_hdlist_or_synthesis($urpm, $medium); - } - - 1; -} - -sub _parse_hdlist { - my ($urpm, $medium, $hdlist_file, %args) = @_; - - $urpm->{log}(N("examining hdlist file [%s]", $hdlist_file)); - ($medium->{start}, $medium->{end}) = $urpm->parse_hdlist($hdlist_file, packing => 1, %args); -} - -sub _parse_synthesis { - my ($urpm, $medium, $synthesis_file, %args) = @_; - - $urpm->{log}(N("examining synthesis file [%s]", $synthesis_file)); - ($medium->{start}, $medium->{end}) = $urpm->parse_synthesis($synthesis_file, %args); -} - -sub is_valid_medium { - my ($medium) = @_; - defined $medium->{start} && defined $medium->{end}; -} - -sub _check_after_reading_hdlist_or_synthesis { - my ($urpm, $medium) = @_; - - if (!is_valid_medium($medium)) { - $urpm->{error}(N("problem reading hdlist or synthesis file of medium \"%s\"", $medium->{name})); - $medium->{ignore} = 1; - } -} sub db_open_or_die { - my ($urpm, $root, $b_force) = @_; + my ($urpm, $root, $b_write_perm) = @_; + + $urpm->{debug} and $urpm->{debug}("opening rpmdb (root=$root, write=$b_write_perm)"); - my $db = URPM::DB::open($root, $b_force) + my $db = URPM::DB::open($root, $b_write_perm || 0) or $urpm->{fatal}(9, N("unable to open rpmdb")); $db; } -sub _update_media__sync_file { - my ($urpm, $medium, $name, $options) = @_; - - my $local_name = $name . _hdlist_suffix($medium); - - foreach (reduce_pathname("$medium->{url}/$medium->{with_hdlist}/../$local_name"), - reduce_pathname("$medium->{url}/$name")) { - eval { - sync_webfetch($urpm, $medium, [$_], $options, quiet => 1); - - if ($local_name ne $name && -s "$urpm->{cachedir}/partial/$local_name") { - rename("$urpm->{cachedir}/partial/$local_name", - "$urpm->{cachedir}/partial/$name"); - } - }; - $@ and unlink "$urpm->{cachedir}/partial/$name"; - -s "$urpm->{cachedir}/partial/$name" and last; - } -} - -sub recursive_find_rpm_files { - my ($dir) = @_; - - my %f; - local $_; #- help perl_checker not warning "undeclared $_" in wanted callback below - File::Find::find( - { - wanted => sub { -f $_ && /\.rpm$/ and $f{"$File::Find::dir/$_"} = 1 }, - follow_skip => 2, - follow_fast => 1, - }, - $dir, - ); - keys %f; -} - -sub clean_dir { - my ($dir) = @_; - - require File::Path; - File::Path::rmtree([$dir]); - mkdir $dir, 0755; -} - -sub _update_medium__get_descriptions_local { - my ($urpm, $medium) = @_; - - unlink statedir_descriptions($urpm, $medium); - - my $dir = file_from_local_url($medium->{url}); - my $description_file = "$dir/media_info/descriptions"; #- new default location - -e $description_file or $description_file = "$dir/../descriptions"; - -e $description_file or return; - - $urpm->{log}(N("copying description file of \"%s\"...", $medium->{name})); - if (copy_and_own($description_file, statedir_descriptions($urpm, $medium))) { - $urpm->{log}(N("...copying done")); - } else { - $urpm->{error}(N("...copying failed")); - $medium->{ignore} = 1; - } -} -sub _update_medium__get_descriptions_remote { - my ($urpm, $medium, $options) = @_; - - unlink "$urpm->{cachedir}/partial/descriptions"; - - if (-e statedir_descriptions($urpm, $medium)) { - urpm::util::move(statedir_descriptions($urpm, $medium), "$urpm->{cachedir}/partial/descriptions"); - } - eval { - sync_webfetch($urpm, $medium, [ reduce_pathname("$medium->{url}/media_info/descriptions") ], - $options, quiet => 1); - }; - #- It is possible that the original fetch of the descriptions - #- failed, but the file still remains in partial/ because it was - #- moved from $urpm->{statedir} earlier. So we need to check if - #- the previous download failed. - if ($@ || ! -e "$urpm->{cachedir}/partial/descriptions") { - eval { - #- try older location - sync_webfetch($urpm, $medium, [ reduce_pathname("$medium->{url}/../descriptions") ], - $options, quiet => 1); - }; - } - if (-e "$urpm->{cachedir}/partial/descriptions") { - urpm::util::move("$urpm->{cachedir}/partial/descriptions", statedir_descriptions($urpm, $medium)); - } -} - -sub _update_medium_first_pass__local { - my ($urpm, $medium, $second_pass, $clean_cache, $retrieved_md5sum, $rpm_files, $options) = @_; - - my $dir = file_from_local_url($medium->{url}); - - #- try to figure a possible hdlist_path (or parent directory of searched directory). - #- this is used to probe for a possible hdlist file. - my $with_hdlist_dir = reduce_pathname($dir . ($medium->{with_hdlist} ? "/$medium->{with_hdlist}" : "/..")); - - #- the directory given does not exist and may be accessible - #- by mounting some other directory. Try to figure it out and mount - #- everything that might be necessary. - -d $dir or $urpm->try_mounting( - $options->{force} < 2 && ($options->{probe_with} || $medium->{with_hdlist}) - ? $with_hdlist_dir : $dir, - #- in case of an iso image, pass its name - is_iso($medium->{removable}) && $medium->{removable}, - ) or $urpm->{error}(N("unable to access medium \"%s\", -this could happen if you mounted manually the directory when creating the medium.", $medium->{name})), return 'unmodified'; - - #- try to probe for possible with_hdlist parameter, unless - #- it is already defined (and valid). - if ($options->{probe_with} && (!$medium->{with_hdlist} || ! -e "$dir/$medium->{with_hdlist}")) { - foreach (_probe_with_try_list(_guess_hdlist_suffix($dir), $options->{probe_with})) { - if (file_size("$dir/$_") > 32) { - $medium->{with_hdlist} = $_; - last; - } - } - #- redo... - $with_hdlist_dir = reduce_pathname($dir . ($medium->{with_hdlist} ? "/$medium->{with_hdlist}" : "/..")); - } - - if ($medium->{virtual}) { - #- syncing a virtual medium is very simple, just try to read the file in order to - #- determine its type, once a with_hdlist has been found (but is mandatory). - _update_media__virtual($urpm, $medium, $with_hdlist_dir); - } - - _update_medium__get_descriptions_local($urpm, $medium); - - #- examine if a distant MD5SUM file is available. - #- this will only be done if $with_hdlist is not empty in order to use - #- an existing hdlist or synthesis file, and to check if download was good. - #- if no MD5SUM is available, do it as before... - #- we can assume at this point a basename is existing, but it needs - #- to be checked for being valid, nothing can be deduced if no MD5SUM - #- file is present. - - my $error; - - unless ($medium->{virtual}) { - if ($medium->{with_hdlist}) { - if (!$options->{nomd5sum} && file_size(reduce_pathname("$with_hdlist_dir/../MD5SUM")) > 32) { - recompute_local_md5sum($urpm, $medium, $options->{force}); - if ($medium->{md5sum}) { - $$retrieved_md5sum = parse_md5sum($urpm, reduce_pathname("$with_hdlist_dir/../MD5SUM"), basename($with_hdlist_dir)); - _read_existing_synthesis_and_hdlist_if_same_md5sum($urpm, $medium, $$retrieved_md5sum) - and return 'unmodified'; - } - } - - #- if the source hdlist is present and we are not forcing using rpm files - if ($options->{force} < 2 && -e $with_hdlist_dir) { - unlink cachedir_hdlist($urpm, $medium); - $urpm->{log}(N("copying source hdlist (or synthesis) of \"%s\"...", $medium->{name})); - $options->{callback} and $options->{callback}('copy', $medium->{name}); - if (copy_and_own($with_hdlist_dir, cachedir_hdlist($urpm, $medium))) { - $options->{callback} and $options->{callback}('done', $medium->{name}); - $urpm->{log}(N("...copying done")); - } else { - $options->{callback} and $options->{callback}('failed', $medium->{name}); - #- force error, reported afterwards - unlink cachedir_hdlist($urpm, $medium); - } - } - - file_size(cachedir_hdlist($urpm, $medium)) > 32 or - $error = 1, $urpm->{error}(N("copy of [%s] failed (file is suspiciously small)", - cachedir_hdlist($urpm, $medium))); - - #- keep checking md5sum of file just copied ! (especially on nfs or removable device). - if (!$error && $$retrieved_md5sum) { - $urpm->{log}(N("computing md5sum of copied source hdlist (or synthesis)")); - md5sum(cachedir_hdlist($urpm, $medium)) eq $$retrieved_md5sum or - $error = 1, $urpm->{error}(N("copy of [%s] failed (md5sum mismatch)", $with_hdlist_dir)); - } - - #- check if the files are equal... and no force copy... - if (!$error && !$options->{force} && -e statedir_synthesis($urpm, $medium)) { - _read_existing_synthesis_and_hdlist_if_same_time_and_msize($urpm, $medium, $medium->{hdlist}) - and return 'unmodified'; - } - } else { - $error = 1; - } - - #- if copying hdlist has failed, try to build it directly. - if ($error) { - if ($urpm->{options}{norebuild}) { - $urpm->{error}(N("unable to access hdlist file of \"%s\", medium ignored", $medium->{name})); - $medium->{ignore} = 1; - } else { - $options->{force} < 2 and $options->{force} = 2; - #- clear error state now. - $error = undef; - } - } - - if ($options->{force} < 2) { - #- examine if a local list file is available (always probed according to with_hdlist) - #- and check hdlist wasn't named very strangely... - if ($medium->{hdlist} ne 'list') { - my $local_list = 'list' . _hdlist_suffix($medium); - my $path_list = reduce_pathname("$with_hdlist_dir/../$local_list"); - -e $path_list or $path_list = "$dir/list"; - if (-e $path_list) { - copy_and_own($path_list, "$urpm->{cachedir}/partial/list") - or do { $urpm->{error}(N("...copying failed")); $error = 1 }; - } - } - } else { - push @$rpm_files, recursive_find_rpm_files($dir); - - #- check files contains something good! - if (@$rpm_files > 0) { - #- we need to rebuild from rpm files the hdlist. - eval { - $urpm->{log}(N("reading rpm files from [%s]", $dir)); - my @unresolved_before = grep { - ! defined $urpm->{provides}{$_}; - } keys %{$urpm->{provides} || {}}; - $medium->{start} = @{$urpm->{depslist}}; - $medium->{headers} = [ $urpm->parse_rpms_build_headers( - dir => "$urpm->{cachedir}/headers", - rpms => $rpm_files, - clean => $$clean_cache, - ) ]; - $medium->{end} = $#{$urpm->{depslist}}; - if ($medium->{start} > $medium->{end}) { - #- an error occured (provided there are files in input.) - delete $medium->{start}; - delete $medium->{end}; - $urpm->{fatal}(9, N("no rpms read")); - } else { - #- make sure the headers will not be removed for another media. - $$clean_cache = 0; - my @unresolved = grep { - ! defined $urpm->{provides}{$_}; - } keys %{$urpm->{provides} || {}}; - @unresolved_before == @unresolved or $$second_pass = 1; - } - }; - $@ and $error = 1, $urpm->{error}(N("unable to read rpm files from [%s]: %s", $dir, $@)); - $error and delete $medium->{headers}; #- do not propagate these. - $error or delete $medium->{synthesis}; #- when building hdlist by ourself, drop synthesis property. - } else { - $error = 1; - $urpm->{error}(N("no rpm files found from [%s]", $dir)); - $medium->{ignore} = 1; - } - } - } - - #- examine if a local pubkey file is available. - if (!$options->{nopubkey} && $medium->{hdlist} ne 'pubkey' && !$medium->{'key-ids'}) { - my $path_pubkey = reduce_pathname("$with_hdlist_dir/../pubkey" . _hdlist_suffix($medium)); - -e $path_pubkey or $path_pubkey = "$dir/pubkey"; - if ($path_pubkey) { - copy_and_own($path_pubkey, "$urpm->{cachedir}/partial/pubkey") - or do { $urpm->{error}(N("...copying failed")) }; - } - } - - $error; -} - -sub _update_medium_first_pass__remote { - my ($urpm, $medium, $retrieved_md5sum, $options) = @_; - my ($error, $basename); - - _update_medium__get_descriptions_remote($urpm, $medium, $options); - - #- examine if a distant MD5SUM file is available. - #- this will only be done if $with_hdlist is not empty in order to use - #- an existing hdlist or synthesis file, and to check if download was good. - #- if no MD5SUM is available, do it as before... - if ($medium->{with_hdlist}) { - #- we can assume at this point a basename is existing, but it needs - #- to be checked for being valid, nothing can be deduced if no MD5SUM - #- file is present. - $basename = basename($medium->{with_hdlist}); - - unlink "$urpm->{cachedir}/partial/MD5SUM"; - eval { - if (!$options->{nomd5sum}) { - sync_webfetch($urpm, $medium, - [ reduce_pathname("$medium->{url}/$medium->{with_hdlist}/../MD5SUM") ], - $options, quiet => 1); - } - }; - if (!$@ && file_size("$urpm->{cachedir}/partial/MD5SUM") > 32) { - recompute_local_md5sum($urpm, $medium, $options->{force} >= 2); - if ($medium->{md5sum}) { - $$retrieved_md5sum = parse_md5sum($urpm, "$urpm->{cachedir}/partial/MD5SUM", $basename); - _read_existing_synthesis_and_hdlist_if_same_md5sum($urpm, $medium, $$retrieved_md5sum) - and return 'unmodified'; - } - } else { - #- at this point, we don't if a basename exists and is valid, let probe it later. - $basename = undef; - } - } - - #- try to probe for possible with_hdlist parameter, unless - #- it is already defined (and valid). - $urpm->{log}(N("retrieving source hdlist (or synthesis) of \"%s\"...", $medium->{name})); - $options->{callback} and $options->{callback}('retrieve', $medium->{name}); - if ($options->{probe_with}) { - my @probe_list = ( - $medium->{with_hdlist} - ? $medium->{with_hdlist} - : _probe_with_try_list(_guess_hdlist_suffix($medium->{url}), $options->{probe_with}) - ); - foreach my $with_hdlist (@probe_list) { - $basename = basename($with_hdlist) or next; - $options->{force} and unlink "$urpm->{cachedir}/partial/$basename"; - eval { - sync_webfetch($urpm, $medium, [ reduce_pathname("$medium->{url}/$with_hdlist") ], - $options, callback => $options->{callback}); - }; - if (!$@ && file_size("$urpm->{cachedir}/partial/$basename") > 32) { - $medium->{with_hdlist} = $with_hdlist; - $urpm->{log}(N("found probed hdlist (or synthesis) as %s", $medium->{with_hdlist})); - last; #- found a suitable with_hdlist in the list above. - } - } - } else { - $basename = basename($medium->{with_hdlist}); - - if ($options->{force}) { - unlink "$urpm->{cachedir}/partial/$basename"; - } else { - #- try to sync (copy if needed) local copy after restored the previous one. - #- this is useful for rsync (?) - if (-e statedir_hdlist_or_synthesis($urpm, $medium)) { - copy_and_own( - statedir_hdlist_or_synthesis($urpm, $medium), - "$urpm->{cachedir}/partial/$basename", - ) or $urpm->{error}(N("...copying failed")), $error = 1; - } - } - eval { - sync_webfetch($urpm, $medium, [ reduce_pathname("$medium->{url}/$medium->{with_hdlist}") ], - $options, callback => $options->{callback}); - }; - if ($@) { - $urpm->{error}(N("...retrieving failed: %s", $@)); - unlink "$urpm->{cachedir}/partial/$basename"; - } - } - - #- check downloaded file has right signature. - if (file_size("$urpm->{cachedir}/partial/$basename") > 32 && $$retrieved_md5sum) { - $urpm->{log}(N("computing md5sum of retrieved source hdlist (or synthesis)")); - unless (md5sum("$urpm->{cachedir}/partial/$basename") eq $$retrieved_md5sum) { - $urpm->{error}(N("...retrieving failed: md5sum mismatch")); - unlink "$urpm->{cachedir}/partial/$basename"; - } - } - - if (file_size("$urpm->{cachedir}/partial/$basename") > 32) { - $options->{callback} and $options->{callback}('done', $medium->{name}); - $urpm->{log}(N("...retrieving done")); - - unless ($options->{force}) { - _read_existing_synthesis_and_hdlist_if_same_time_and_msize($urpm, $medium, $basename) - and return 'unmodified'; - } - - #- the files are different, update local copy. - rename("$urpm->{cachedir}/partial/$basename", cachedir_hdlist($urpm, $medium)); - - #- retrieval of hdlist or synthesis has been successful, - #- check whether a list file is available. - #- and check hdlist wasn't named very strangely... - if ($medium->{hdlist} ne 'list') { - _update_media__sync_file($urpm, $medium, 'list', $options); - } - - #- retrieve pubkey file. - if (!$options->{nopubkey} && $medium->{hdlist} ne 'pubkey' && !$medium->{'key-ids'}) { - _update_media__sync_file($urpm, $medium, 'pubkey', $options); - } - } else { - $error = 1; - $options->{callback} and $options->{callback}('failed', $medium->{name}); - $urpm->{error}(N("retrieval of source hdlist (or synthesis) failed")); - } - $error; -} - -sub _read_cachedir_pubkey { - my ($urpm, $medium) = @_; - -s "$urpm->{cachedir}/partial/pubkey" or return; - - $urpm->{log}(N("examining pubkey file of \"%s\"...", $medium->{name})); - - my %key_ids; - $urpm->import_needed_pubkeys( - [ $urpm->parse_armored_file("$urpm->{cachedir}/partial/pubkey") ], - root => $urpm->{root}, - callback => sub { - my (undef, undef, $_k, $id, $imported) = @_; - if ($id) { - $key_ids{$id} = undef; - $imported and $urpm->{log}(N("...imported key %s from pubkey file of \"%s\"", - $id, $medium->{name})); - } else { - $urpm->{error}(N("unable to import pubkey file of \"%s\"", $medium->{name})); - } - }); - if (keys(%key_ids)) { - $medium->{'key-ids'} = join(',', keys %key_ids); - } -} - -sub _update_medium_first_pass { - my ($urpm, $medium, $second_pass, $clean_cache, %options) = @_; - - $medium->{ignore} and return; - - $options{forcekey} and delete $medium->{'key-ids'}; - - #- we should create the associated synthesis file if it does not already exist... - file_size(statedir_synthesis($urpm, $medium)) > 32 - or $medium->{modified_synthesis} = 1; - - if ($medium->{static}) { - #- don't ever update static media - $medium->{modified} = 0; - } elsif ($options{all}) { - #- if we're rebuilding all media, mark them as modified (except removable ones) - $medium->{modified} ||= $medium->{url} !~ m!^removable!; - } - - unless ($medium->{modified}) { - #- the medium is not modified, but to compute dependencies, - #- we still need to read it and all synthesis will be written if - #- an unresolved provides is found. - #- to speed up the process, we only read the synthesis at the beginning. - _update_media__when_not_modified($urpm, $medium); - return; - } - - #- always delete a remaining list file or pubkey file in cache. - foreach (qw(list pubkey)) { - unlink "$urpm->{cachedir}/partial/$_"; - } - - #- check for a reconfig.urpmi file (if not already reconfigured) - if (!$medium->{noreconfigure}) { - may_reconfig_urpmi($urpm, $medium, \%options); - } - - #- list of rpm files for this medium, only available for local medium where - #- the source hdlist is not used (use force). - my ($error, $retrieved_md5sum, @files); - - #- check if the medium is using a local or a removable medium. - if (file_from_local_url($medium->{url})) { - my $rc = _update_medium_first_pass__local($urpm, $medium, $second_pass, $clean_cache, \$retrieved_md5sum, \@files, \%options); - if ($rc eq 'unmodified') { - return; - } else { - $error = $rc; - } - } else { - my $rc = _update_medium_first_pass__remote($urpm, $medium, \$retrieved_md5sum, \%options); - if ($rc eq 'unmodified') { - return; - } else { - $error = $rc; - } - } - - #- build list file according to hdlist. - unless ($medium->{headers} || file_size(cachedir_hdlist($urpm, $medium)) > 32) { - $error = 1; - $urpm->{error}(N("no hdlist file found for medium \"%s\"", $medium->{name})); - } - - unless ($error || $medium->{virtual}) { - #- sort list file contents according to id. - my %list; - if ($medium->{headers}) { - my $protocol = protocol_from_url($medium->{url}); - - #- rpm files have already been read (first pass), there is just a need to - #- build list hash. - foreach (@files) { - m|/([^/]*\.rpm)$| or next; - $list{$1} and $urpm->{error}(N("file [%s] already used in the same medium \"%s\"", $1, $medium->{name})), next; - $list{$1} = "$protocol:/$_\n"; - } - } else { - #- read first pass hdlist or synthesis, try to open as synthesis, if file - #- is larger than 1MB, this is probably an hdlist else a synthesis. - #- anyway, if one tries fails, try another mode. - $options{callback} and $options{callback}('parse', $medium->{name}); - my @unresolved_before = grep { ! defined $urpm->{provides}{$_} } keys %{$urpm->{provides} || {}}; - if (!$medium->{synthesis} - || file_size(cachedir_hdlist($urpm, $medium)) > 262144) { - _parse_hdlist($urpm, $medium, cachedir_hdlist($urpm, $medium)); - if (is_valid_medium($medium)) { - delete $medium->{synthesis}; - } else { - _parse_synthesis($urpm, $medium, cachedir_hdlist($urpm, $medium)); - is_valid_medium($medium) and $medium->{synthesis} = 1; - } - } else { - _parse_synthesis($urpm, $medium, cachedir_hdlist($urpm, $medium)); - if (is_valid_medium($medium)) { - $medium->{synthesis} = 1; - } else { - _parse_hdlist($urpm, $medium, cachedir_hdlist($urpm, $medium)); - is_valid_medium($medium) and delete $medium->{synthesis}; - } - } - if (is_valid_medium($medium)) { - $options{callback} && $options{callback}('done', $medium->{name}); - } else { - $error = 1; - $urpm->{error}(N("unable to parse hdlist file of \"%s\"", $medium->{name})); - $options{callback} && $options{callback}('failed', $medium->{name}); - #- we will have to read back the current synthesis file unmodified. - } - - unless ($error) { - my @unresolved_after = grep { ! defined $urpm->{provides}{$_} } keys %{$urpm->{provides} || {}}; - @unresolved_before == @unresolved_after or $$second_pass = 1; - - if ($medium->{hdlist} ne 'list' && -s "$urpm->{cachedir}/partial/list") { - if (open my $fh, "$urpm->{cachedir}/partial/list") { - local $_; - while (<$fh>) { - m|/([^/]*\.rpm)$| or next; - $list{$1} and $urpm->{error}(N("file [%s] already used in the same medium \"%s\"", $1, $medium->{name})), next; - $list{$1} = "$medium->{url}/$_"; - } - close $fh; - } - } else { - #- if url is clear and no relative list file has been downloaded, - #- there is no need for a list file. - if ($medium->{url} ne $medium->{clear_url}) { - foreach ($medium->{start} .. $medium->{end}) { - my $filename = $urpm->{depslist}[$_]->filename; - $list{$filename} = "$medium->{url}/$filename\n"; - } - } - } - } - } - - unless ($error) { - if (keys %list) { - #- write list file. - #- make sure group and other do not have any access to this file, used to hide passwords. - if ($medium->{list}) { - my $mask = umask 077; - open my $listfh, ">", cachedir_list($urpm, $medium) - or $error = 1, $urpm->{error}(N("unable to write list file of \"%s\"", $medium->{name})); - umask $mask; - print $listfh values %list; - close $listfh; - } - - #- check if at least something has been written into list file. - if ($medium->{list} && -s cachedir_list($urpm, $medium)) { - $urpm->{log}(N("writing list file for medium \"%s\"", $medium->{name})); - } else { - $error = 1, $urpm->{error}(N("nothing written in list file for \"%s\"", $medium->{name})); - } - } else { - #- the flag is no longer necessary. - if ($medium->{list}) { - unlink statedir_list($urpm, $medium); - delete $medium->{list}; - } - } - } - } - - unless ($error) { - #- now... on pubkey - _read_cachedir_pubkey($urpm, $medium); - } - - unless ($medium->{virtual}) { - if ($error) { - #- an error has occured for updating the medium, we have to remove temporary files. - unlink cachedir_hdlist($urpm, $medium); - $medium->{list} and unlink cachedir_list($urpm, $medium); - #- read default synthesis (we have to make sure nothing get out of depslist). - _parse_synthesis($urpm, $medium, statedir_synthesis($urpm, $medium)); - if (!is_valid_medium($medium)) { - $urpm->{error}(N("problem reading synthesis file of medium \"%s\"", $medium->{name})); - $medium->{ignore} = 1; - } - } else { - #- make sure to rebuild base files and clear medium modified state. - $medium->{modified} = 0; - $urpm->{md5sum_modified} = 1; - - #- but use newly created file. - unlink statedir_hdlist($urpm, $medium); - $medium->{synthesis} and unlink statedir_synthesis($urpm, $medium); - $medium->{list} and unlink statedir_list($urpm, $medium); - unless ($medium->{headers}) { - unlink statedir_synthesis($urpm, $medium); - unlink statedir_hdlist($urpm, $medium); - urpm::util::move(cachedir_hdlist($urpm, $medium), - statedir_hdlist_or_synthesis($urpm, $medium)); - } - if ($medium->{list}) { - urpm::util::move(cachedir_list($urpm, $medium), statedir_list($urpm, $medium)); - } - $medium->{md5sum} = $retrieved_md5sum; #- anyway, keep it, the previous one is no longer useful. - - #- and create synthesis file associated. - $medium->{modified_synthesis} = !$medium->{synthesis}; - } - } -} - -#- take care of modified medium only, or all if all have to be recomputed. -sub _update_medium_second_pass { - my ($urpm, $medium, $second_pass, $callback) = @_; - - $medium->{ignore} and return; - - $callback and $callback->('parse', $medium->{name}); - #- a modified medium is an invalid medium, we have to read back the previous hdlist - #- or synthesis which has not been modified by first pass above. - if ($medium->{headers} && !$medium->{modified}) { - if ($second_pass) { - $urpm->{log}(N("reading headers from medium \"%s\"", $medium->{name})); - ($medium->{start}, $medium->{end}) = $urpm->parse_headers(dir => "$urpm->{cachedir}/headers", - headers => $medium->{headers}, - ); - } - $urpm->{log}(N("building hdlist [%s]", statedir_hdlist($urpm, $medium))); - #- finish building operation of hdlist. - $urpm->build_hdlist(start => $medium->{start}, - end => $medium->{end}, - dir => "$urpm->{cachedir}/headers", - hdlist => statedir_hdlist($urpm, $medium), - ); - #- synthesis needs to be created, since the medium has been built from rpm files. - eval { $urpm->build_synthesis( - start => $medium->{start}, - end => $medium->{end}, - synthesis => statedir_synthesis($urpm, $medium), - ) }; - if ($@) { - #- XXX this happens when building a synthesis for a local media from RPMs... why ? - $urpm->{error}(N("Unable to build synthesis file for medium \"%s\". Your hdlist file may be corrupted.", $medium->{name})); - $urpm->{error}($@); - unlink statedir_synthesis($urpm, $medium); - } else { - $urpm->{log}(N("built hdlist synthesis file for medium \"%s\"", $medium->{name})); - } - #- keep in mind we have a modified database, sure at this point. - $urpm->{md5sum_modified} = 1; - } elsif ($medium->{synthesis}) { - if ($second_pass) { - if ($medium->{virtual}) { - if (file_from_file_url($medium->{url})) { - _parse_synthesis($urpm, $medium, hdlist_or_synthesis_for_virtual_medium($medium)); - } - } else { - _parse_synthesis($urpm, $medium, statedir_synthesis($urpm, $medium)); - } - } - } else { - if ($second_pass) { - _parse_hdlist($urpm, $medium, statedir_hdlist($urpm, $medium)); - } - #- check if the synthesis file can be built. - if (($second_pass || $medium->{modified_synthesis}) && !$medium->{modified}) { - unless ($medium->{virtual}) { - eval { $urpm->build_synthesis( - start => $medium->{start}, - end => $medium->{end}, - synthesis => statedir_synthesis($urpm, $medium), - ) }; - if ($@) { - $urpm->{error}(N("Unable to build synthesis file for medium \"%s\". Your hdlist file may be corrupted.", $medium->{name})); - $urpm->{error}($@); - unlink statedir_synthesis($urpm, $medium); - } else { - $urpm->{log}(N("built hdlist synthesis file for medium \"%s\"", $medium->{name})); - } - } - #- keep in mind we have modified database, sure at this point. - $urpm->{md5sum_modified} = 1; - } - } - $callback && $callback->('done', $medium->{name}); -} - sub remove_obsolete_headers_in_cache { my ($urpm) = @_; my %headers; - my $dh = $urpm->opendir_safe("$urpm->{cachedir}/headers"); - if ($dh) { + if (my $dh = urpm::sys::opendir_safe($urpm, "$urpm->{cachedir}/headers")) { local $_; while (defined($_ = readdir $dh)) { m|^([^/]*-[^-]*-[^-]*\.[^\.]*)(?::\S*)?$| and $headers{$1} = $_; } - closedir $dh; } if (%headers) { - $urpm->{log}(N("found %d headers in cache", scalar(keys %headers))); + my $previous_total = scalar(keys %headers); foreach (@{$urpm->{depslist}}) { delete $headers{$_->fullname}; } - $urpm->{log}(N("removing %d obsolete headers in cache", scalar(keys %headers))); + $urpm->{log}(N("found %d rpm headers in cache, removing %d obsolete headers", $previous_total, scalar(keys %headers))); foreach (values %headers) { unlink "$urpm->{cachedir}/headers/$_"; } } } -#- Update the urpmi database w.r.t. the current configuration. -#- Takes care of modifications, and tries some tricks to bypass -#- the recomputation of base files. -#- Recognized options : -#- all : all medias are being rebuilt -#- callback : UI callback -#- compress : use compressed download (for rsync) -#- forcekey : force retrieval of pubkey -#- force : try to force rebuilding base files (1) or hdlist from rpm files (2) -#- limit_rate : download limit rate -#- noclean : keep old files in the header cache directory -#- nolock : don't lock the urpmi database -#- nomd5sum : don't verify MD5SUM of retrieved files -#- nopubkey : don't use rpm pubkeys -#- norebuild : don't try to rebuild hdlists from rpm headers -#- probe_with : probe synthesis or hdlist (or none) -#- quiet : download hdlists quietly -#- ratio : use this compression ratio (with gzip, default is 4) -sub update_media { - my ($urpm, %options) = @_; - - $urpm->{media} or return; # verify that configuration has been read - - $options{nopubkey} ||= $urpm->{options}{nopubkey}; - #- get gpg-pubkey signature. - if (!$options{nopubkey}) { - $urpm->lock_rpm_db('exclusive'); - $urpm->{keys} or $urpm->parse_pubkeys(root => $urpm->{root}); - } - #- lock database if allowed. - $urpm->lock_urpmi_db('exclusive') if !$options{nolock}; - - #- examine each medium to see if one of them needs to be updated. - #- if this is the case and if not forced, try to use a pre-calculated - #- hdlist file, else build it from rpm files. - $urpm->clean; - - my $clean_cache = !$options{noclean}; - my $second_pass; - foreach my $medium (@{$urpm->{media}}) { - _update_medium_first_pass($urpm, $medium, \$second_pass, \$clean_cache, %options); - } - - #- some unresolved provides may force to rebuild all synthesis, - #- a second pass will be necessary. - if ($second_pass) { - $urpm->{log}(N("performing second pass to compute dependencies\n")); - $urpm->unresolved_provides_clean; - } - - #- second pass consists in reading again synthesis or hdlists. - foreach my $medium (@{$urpm->{media}}) { - _update_medium_second_pass($urpm, $medium, $second_pass, $options{callback}); - } - - if ($urpm->{modified}) { - if ($options{noclean}) { - #- clean headers cache directory to remove everything that is no longer - #- useful according to the depslist. - remove_obsolete_headers_in_cache($urpm); - } - #- write config files in any case - $urpm->write_config; - dump_proxy_config(); - } elsif ($urpm->{md5sum_modified}) { - #- NB: in case of $urpm->{modified}, write_MD5SUM is called in write_config above - write_MD5SUM($urpm); - } - - generate_media_names($urpm); - - $options{nolock} or $urpm->unlock_urpmi_db; - $options{nopubkey} or $urpm->unlock_rpm_db; -} - -#- clean params and depslist computation zone. -sub clean { - my ($urpm) = @_; - - $urpm->{depslist} = []; - $urpm->{provides} = {}; - - foreach (@{$urpm->{media} || []}) { - delete $_->{start}; - delete $_->{end}; - } -} - -sub try_mounting { - my ($urpm, $dir, $o_removable) = @_; - my %infos; - - my $is_iso = is_iso($o_removable); - my @mntpoints = $is_iso - #- note: for isos, we don't parse the fstab because it might not be declared in it. - #- so we try to remove suffixes from the dir name until the dir exists - ? ($dir = urpm::sys::trim_until_d($dir)) - : urpm::sys::find_mntpoints($dir = reduce_pathname($dir), \%infos); - foreach (grep { - ! $infos{$_}{mounted} && $infos{$_}{fs} ne 'supermount'; - } @mntpoints) - { - $urpm->{log}(N("mounting %s", $_)); - if ($is_iso) { - #- to mount an iso image, grab the first loop device - my $loopdev = urpm::sys::first_free_loopdev(); - sys_log("mount iso $_ on $o_removable"); - $loopdev and system('mount', $o_removable, $_, '-t', 'iso9660', '-o', "loop=$loopdev"); - } else { - sys_log("mount $_"); - system("mount '$_' 2>/dev/null"); - } - $o_removable && $infos{$_}{fs} ne 'supermount' and $urpm->{removable_mounted}{$_} = undef; - } - -e $dir; -} - -sub try_umounting { - my ($urpm, $dir) = @_; - my %infos; - - $dir = reduce_pathname($dir); - foreach (reverse grep { - $infos{$_}{mounted} && $infos{$_}{fs} ne 'supermount'; - } urpm::sys::find_mntpoints($dir, \%infos)) - { - $urpm->{log}(N("unmounting %s", $_)); - sys_log("umount $_"); - system("umount '$_' 2>/dev/null"); - delete $urpm->{removable_mounted}{$_}; - } - ! -e $dir; -} - -sub try_umounting_removables { - my ($urpm) = @_; - foreach (keys %{$urpm->{removable_mounted}}) { - $urpm->try_umounting($_); - } - delete $urpm->{removable_mounted}; -} - #- register local packages for being installed, keep track of source. sub register_rpms { my ($urpm, @files) = @_; @@ -2151,13 +129,11 @@ sub register_rpms { if (protocol_from_url($_)) { my $basename = basename($_); unlink "$urpm->{cachedir}/partial/$basename"; - eval { - $urpm->{log}(N("retrieving rpm file [%s] ...", $_)); - sync_webfetch($urpm, undef, [$_], { quiet => 1 }); + $urpm->{log}(N("retrieving rpm file [%s] ...", $_)); + if (urpm::download::sync($urpm, undef, [$_], quiet => 1)) { $urpm->{log}(N("...retrieving done")); $_ = "$urpm->{cachedir}/partial/$basename"; - }; - if ($@) { + } else { $urpm->{error}(N("...retrieving failed: %s", $@)); unlink "$urpm->{cachedir}/partial/$basename"; next; @@ -2193,420 +169,6 @@ sub register_rpms { %requested; } -sub _findindeps { - my ($urpm, $found, $qv, $v, %options) = @_; - - foreach (keys %{$urpm->{provides}}) { - #- search through provides to find if a provide matches this one; - #- but manage choices correctly (as a provides may be virtual or - #- defined several times). - /$qv/ || !$options{caseinsensitive} && /$qv/i or next; - - my @list = grep { defined $_ } map { - my $pkg = $urpm->{depslist}[$_]; - $pkg && ($options{src} ? $pkg->arch eq 'src' : $pkg->arch ne 'src') - ? $pkg->id : undef; - } keys %{$urpm->{provides}{$_} || {}}; - @list > 0 and push @{$found->{$v}}, join '|', @list; - } -} - -#- search packages registered by their names by storing their ids into the $packages hash. -sub search_packages { - my ($urpm, $packages, $names, %options) = @_; - my (%exact, %exact_a, %exact_ra, %found, %foundi); - foreach my $v (@$names) { - my $qv = quotemeta $v; - $qv = '(?i)' . $qv if $options{caseinsensitive}; - - unless ($options{fuzzy}) { - #- try to search through provides. - if (my @l = map { - $_ - && ($options{src} ? $_->arch eq 'src' : $_->is_arch_compat) - && ($options{use_provides} || $_->name eq $v) - && defined($_->id) - && (!defined $urpm->{searchmedia} || - $urpm->{searchmedia}{start} <= $_->id - && $urpm->{searchmedia}{end} >= $_->id) - ? $_ : @{[]}; - } map { - $urpm->{depslist}[$_]; - } keys %{$urpm->{provides}{$v} || {}}) - { - #- we assume that if there is at least one package providing - #- the resource exactly, this should be the best one; but we - #- first check if one of the packages has the same name as searched. - if (my @l2 = grep { $_->name eq $v } @l) { - @l = @l2; - } - $exact{$v} = join('|', map { $_->id } @l); - next; - } - } - - if ($options{use_provides} && $options{fuzzy}) { - _findindeps($urpm, \%found, $qv, $v, %options); - } - - foreach my $id (defined $urpm->{searchmedia} ? - ($urpm->{searchmedia}{start} .. $urpm->{searchmedia}{end}) : - (0 .. $#{$urpm->{depslist}}) - ) { - my $pkg = $urpm->{depslist}[$id]; - ($options{src} ? $pkg->arch eq 'src' : $pkg->is_arch_compat) or next; - my $pack_name = $pkg->name; - my $pack_ra = $pack_name . '-' . $pkg->version; - my $pack_a = "$pack_ra-" . $pkg->release; - my $pack = "$pack_a." . $pkg->arch; - unless ($options{fuzzy}) { - if ($pack eq $v) { - $exact{$v} = $id; - next; - } elsif ($pack_a eq $v) { - push @{$exact_a{$v}}, $id; - next; - } elsif ($pack_ra eq $v || $options{src} && $pack_name eq $v) { - push @{$exact_ra{$v}}, $id; - next; - } - } - $pack =~ /$qv/ and push @{$found{$v}}, $id; - $pack =~ /$qv/i and push @{$foundi{$v}}, $id unless $options{caseinsensitive}; - } - } - - my $result = 1; - foreach (@$names) { - if (defined $exact{$_}) { - $packages->{$exact{$_}} = 1; - foreach (split /\|/, $exact{$_}) { - my $pkg = $urpm->{depslist}[$_] or next; - $pkg->set_flag_skip(0); #- reset skip flag as manually selected. - } - } else { - #- at this level, we need to search the best package given for a given name, - #- always prefer already found package. - my %l; - foreach (@{$exact_a{$_} || $exact_ra{$_} || $found{$_} || $foundi{$_} || []}) { - my $pkg = $urpm->{depslist}[$_]; - push @{$l{$pkg->name}}, $pkg; - } - if (values(%l) == 0 || values(%l) > 1 && !$options{all}) { - $urpm->{error}(N("no package named %s", $_)); - values(%l) != 0 and $urpm->{error}( - N("The following packages contain %s: %s", - $_, "\n" . join("\n", sort { $a cmp $b } keys %l)) - ); - $result = 0; - } else { - if (!@{$exact_a{$_} || $exact_ra{$_} || []}) { - #- we found a non-exact match - $result = 'substring'; - } - foreach (values %l) { - my $best; - foreach (@$_) { - if ($best && $best != $_) { - $_->compare_pkg($best) > 0 and $best = $_; - } else { - $best = $_; - } - } - $packages->{$best->id} = 1; - $best->set_flag_skip(0); #- reset skip flag as manually selected. - } - } - } - } - - #- return true if no error has been encountered, else false. - $result; -} - -#- Resolves dependencies between requested packages (and auto selection if any). -#- handles parallel option if any. -#- The return value is true if program should be restarted (in order to take -#- care of important packages being upgraded (priority upgrades) -#- %options : -#- rpmdb -#- auto_select -#- callback_choices -#- install_src -#- keep -#- nodeps -#- priority_upgrade -sub resolve_dependencies { - #- $state->{selected} will contain the selection of packages to be - #- installed or upgraded - my ($urpm, $state, $requested, %options) = @_; - my $need_restart; - - if ($options{install_src}) { - #- only src will be installed, so only update $state->{selected} according - #- to src status of files. - foreach (keys %$requested) { - my $pkg = $urpm->{depslist}[$_] or next; - $pkg->arch eq 'src' or next; - $state->{selected}{$_} = undef; - } - } - if ($urpm->{parallel_handler}) { - #- build the global synthesis file first. - my $file = "$urpm->{cachedir}/partial/parallel.cz"; - unlink $file; - foreach (@{$urpm->{media}}) { - is_valid_medium($_) or next; - my $f = statedir_synthesis($urpm, $_); - system "cat '$f' >> '$file'"; - } - #- let each node determine what is requested, according to handler given. - $urpm->{parallel_handler}->parallel_resolve_dependencies($file, $urpm, $state, $requested, %options); - } else { - my $db; - - if ($options{rpmdb}) { - $db = new URPM; - $db->parse_synthesis($options{rpmdb}); - } else { - $db = db_open_or_die($urpm, $urpm->{root}); - } - - my $sig_handler = sub { undef $db; exit 3 }; - local $SIG{INT} = $sig_handler; - local $SIG{QUIT} = $sig_handler; - - #- auto select package for upgrading the distribution. - if ($options{auto_select}) { - $urpm->request_packages_to_upgrade($db, $state, $requested, requested => undef, - start => $urpm->{searchmedia}{start}, end => $urpm->{searchmedia}{end}); - } - - #- resolve dependencies which will be examined for packages that need to - #- have urpmi restarted when they're updated. - $urpm->resolve_requested($db, $state, $requested, %options); - - if ($options{priority_upgrade} && !$options{rpmdb}) { - my (%priority_upgrade, %priority_requested); - @priority_upgrade{split /,/, $options{priority_upgrade}} = (); - - #- check if a priority upgrade should be tried - foreach (keys %{$state->{selected}}) { - my $pkg = $urpm->{depslist}[$_] or next; - exists $priority_upgrade{$pkg->name} or next; - $priority_requested{$pkg->id} = undef; - } - - if (%priority_requested) { - my %priority_state; - - $urpm->resolve_requested($db, \%priority_state, \%priority_requested, %options); - if (grep { ! exists $priority_state{selected}{$_} } keys %priority_requested) { - #- some packages which were selected previously have not been selected, strange! - $need_restart = 0; - } elsif (grep { ! exists $priority_state{selected}{$_} } keys %{$state->{selected}}) { - #- there are other packages to install after this priority transaction. - %$state = %priority_state; - $need_restart = 1; - } - } - } - } - $need_restart; -} - -sub create_transaction { - my ($urpm, $state, %options) = @_; - - if ($urpm->{parallel_handler} || !$options{split_length} || - keys %{$state->{selected}} < $options{split_level}) { - #- build simplest transaction (no split). - $urpm->build_transaction_set(undef, $state, split_length => 0); - } else { - my $db; - - if ($options{rpmdb}) { - $db = new URPM; - $db->parse_synthesis($options{rpmdb}); - } else { - $db = db_open_or_die($urpm, $urpm->{root}); - } - - my $sig_handler = sub { undef $db; exit 3 }; - local $SIG{INT} = $sig_handler; - local $SIG{QUIT} = $sig_handler; - - #- build transaction set... - $urpm->build_transaction_set($db, $state, split_length => $options{split_length}); - } -} - -#- get the list of packages that should not be upgraded or installed, -#- typically from the inst.list or skip.list files. -sub get_packages_list { - my ($file, $o_extra) = @_; - my $val = []; - open my $f, $file or return []; - foreach (<$f>, split /,/, $o_extra || '') { - chomp; s/#.*$//; s/^\s*//; s/\s*$//; - next if $_ eq ''; - push @$val, $_; - } - close $f; - $val; -} - -#- select sources for selected packages, -#- according to keys of the packages hash. -#- returns a list of lists containing the source description for each rpm, -#- matching the exact number of registered media; ignored media being -#- associated to a null list. -sub get_source_packages { - my ($urpm, $packages, %options) = @_; - my (%protected_files, %local_sources, %fullname2id); - - #- build association hash to retrieve id and examine all list files. - foreach (keys %$packages) { - foreach (split /\|/, $_) { - if ($urpm->{source}{$_}) { - $protected_files{$local_sources{$_} = $urpm->{source}{$_}} = undef; - } else { - $fullname2id{$urpm->{depslist}[$_]->fullname} = $_ . ''; - } - } - } - - #- examine each medium to search for packages. - #- now get rpm file name in hdlist to match list file. - my %file2fullnames; - foreach my $pkg (@{$urpm->{depslist} || []}) { - $file2fullnames{$pkg->filename}{$pkg->fullname} = undef; - } - - #- examine the local repository, which is trusted (no gpg or pgp signature check but md5 is now done). - my $dh = $urpm->opendir_safe("$urpm->{cachedir}/rpms"); - if ($dh) { - while (defined(my $filename = readdir $dh)) { - my $filepath = "$urpm->{cachedir}/rpms/$filename"; - if (-d $filepath) { - } elsif ($options{clean_all} || ! -s _) { - unlink $filepath; #- this file should be removed or is already empty. - } else { - if (keys(%{$file2fullnames{$filename} || {}}) > 1) { - $urpm->{error}(N("there are multiple packages with the same rpm filename \"%s\"", $filename)); - } elsif (keys(%{$file2fullnames{$filename} || {}}) == 1) { - my ($fullname) = keys(%{$file2fullnames{$filename} || {}}); - if (defined(my $id = delete $fullname2id{$fullname})) { - $local_sources{$id} = $filepath; - } else { - $options{clean_other} && ! exists $protected_files{$filepath} and unlink $filepath; - } - } else { - $options{clean_other} && ! exists $protected_files{$filepath} and unlink $filepath; - } - } - } - closedir $dh; - } - - if ($options{clean_all}) { - #- clean download directory, do it here even if this is not the best moment. - clean_dir("$urpm->{cachedir}/partial"); - } - - my ($error, @list_error, @list, %examined); - - foreach my $medium (@{$urpm->{media} || []}) { - my (%sources, %list_examined, $list_warning); - - if (is_valid_medium($medium) && !$medium->{ignore}) { - #- always prefer a list file if available. - my $listfile = $medium->{list} ? statedir_list($urpm, $medium) : ''; - if (!$listfile && $medium->{virtual}) { - my $dir = file_from_local_url($medium->{url}); - my $with_hdlist_dir = reduce_pathname($dir . ($medium->{with_hdlist} ? "/$medium->{with_hdlist}" : "/..")); - my $local_list = 'list' . _hdlist_suffix($medium); - $listfile = reduce_pathname("$with_hdlist_dir/../$local_list"); - -s $listfile or $listfile = "$dir/list"; - } - if ($listfile && -r $listfile) { - my $fh = $urpm->open_safe('<', $listfile); - if ($fh) { - local $_; - while (<$fh>) { - chomp; - if (my ($filename) = m|/([^/]*\.rpm)$|) { - if (keys(%{$file2fullnames{$filename} || {}}) > 1) { - $urpm->{error}(N("there are multiple packages with the same rpm filename \"%s\"", $filename)); - next; - } elsif (keys(%{$file2fullnames{$filename} || {}}) == 1) { - my ($fullname) = keys(%{$file2fullnames{$filename} || {}}); - if (defined(my $id = $fullname2id{$fullname})) { - if (!/\.delta\.rpm$/ || $urpm->is_delta_installable($urpm->{depslist}[$id], $options{root})) { - $sources{$id} = $medium->{virtual} ? "$medium->{url}/$_" : $_; - } - } - $list_examined{$fullname} = $examined{$fullname} = undef; - } - } else { - chomp; - $error = 1; - $urpm->{error}(N("unable to correctly parse [%s] on value \"%s\"", $listfile, $_)); - last; - } - } - close $fh; - } - } elsif ($listfile && -e $listfile) { - # list file exists but isn't readable - # report error only if no result found, list files are only readable by root - push @list_error, N("unable to access list file of \"%s\", medium ignored", $medium->{name}); - $< and push @list_error, " " . N("(retry as root?)"); - next; - } - if (defined $medium->{url}) { - foreach ($medium->{start} .. $medium->{end}) { - my $pkg = $urpm->{depslist}[$_]; - my $fi = $pkg->filename; - if (keys(%{$file2fullnames{$fi} || {}}) > 1) { - $urpm->{error}(N("there are multiple packages with the same rpm filename \"%s\"", $fi)); - next; - } elsif (keys(%{$file2fullnames{$fi} || {}}) == 1) { - my ($fullname) = keys(%{$file2fullnames{$fi} || {}}); - unless (exists($list_examined{$fullname})) { - ++$list_warning; - if (defined(my $id = $fullname2id{$fullname})) { - if ($fi !~ /\.delta\.rpm$/ || $urpm->is_delta_installable($urpm->{depslist}[$id], $options{root})) { - $sources{$id} = "$medium->{url}/" . $fi; - } - } - $examined{$fullname} = undef; - } - } - } - $list_warning && $medium->{list} && -r statedir_list($urpm, $medium) && -f _ - and $urpm->{error}(N("medium \"%s\" uses an invalid list file: - mirror is probably not up-to-date, trying to use alternate method", $medium->{name})); - } elsif (!%list_examined) { - $error = 1; - $urpm->{error}(N("medium \"%s\" does not define any location for rpm files", $medium->{name})); - } - } - push @list, \%sources; - } - - #- examine package list to see if a package has not been found. - foreach (grep { ! exists($examined{$_}) } keys %fullname2id) { - # print list errors only once if any - $urpm->{error}($_) foreach @list_error; - @list_error = (); - $error = 1; - $urpm->{error}(N("package %s is not found.", $_)); - } - - $error ? @{[]} : (\%local_sources, \@list); -} - #- checks whether the delta RPM represented by $pkg is installable wrt the #- RPM DB on $root. For this, it extracts the rpm version to which the #- delta applies from the delta rpm filename itself. So naming conventions @@ -2632,766 +194,35 @@ sub download_source_packages { my %sources = %$local_sources; my %error_sources; - $urpm->lock_urpmi_db('exclusive') if !$options{nolock}; - $urpm->copy_packages_of_removable_media($list, \%sources, %options) or return; - $urpm->download_packages_of_distant_media($list, \%sources, \%error_sources, %options); - $urpm->unlock_urpmi_db unless $options{nolock}; + require urpm::get_pkgs; + urpm::removable::copy_packages_of_removable_media($urpm, $list, \%sources, $options{ask_for_medium}) or return; + urpm::get_pkgs::download_packages_of_distant_media($urpm, $list, \%sources, \%error_sources, %options); %sources, %error_sources; } -#- lock policy concerning chroot : -# - lock rpm db in chroot -# - lock urpmi db in / -sub _lock { - my ($urpm, $fh_ref, $file, $b_exclusive) = @_; - #- avoid putting a require on Fcntl ':flock' (which is perl and not perl-base). - my ($LOCK_SH, $LOCK_EX, $LOCK_NB) = (1, 2, 4); - if ($b_exclusive) { - #- lock urpmi database, but keep lock to wait for an urpmi.update to finish. - } else { - #- create the .LOCK file if needed (and if possible) - unless (-e $file) { - open(my $f, ">", $file); - close $f; - } - #- lock urpmi database, if the LOCK file doesn't exists no share lock. - } - my ($sense, $mode) = $b_exclusive ? ('>', $LOCK_EX) : ('<', $LOCK_SH); - open $$fh_ref, $sense, $file or return; - flock $$fh_ref, $mode|$LOCK_NB or $urpm->{fatal}(7, N("urpmi database locked")); -} - -sub lock_rpm_db { - my ($urpm, $b_exclusive) = @_; - _lock($urpm, \$RPMLOCK_FILE, "$urpm->{root}/$urpm->{statedir}/.RPMLOCK", $b_exclusive); -} -sub lock_urpmi_db { - my ($urpm, $b_exclusive) = @_; - _lock($urpm, \$LOCK_FILE, "$urpm->{statedir}/.LOCK", $b_exclusive); -} -#- deprecated -sub exlock_urpmi_db { - my ($urpm) = @_; - lock_urpmi_db($urpm, 'exclusive'); -} - -sub _unlock { - my ($fh_ref) = @_; - #- avoid putting a require on Fcntl ':flock' (which is perl and not perl-base). - my $LOCK_UN = 8; - #- now everything is finished. - #- release lock on database. - flock $$fh_ref, $LOCK_UN; - close $$fh_ref; -} -sub unlock_rpm_db { - my ($_urpm) = @_; - _unlock(\$RPMLOCK_FILE); -} -sub unlock_urpmi_db { - my ($_urpm) = @_; - _unlock(\$LOCK_FILE); -} - -sub copy_packages_of_removable_media { - my ($urpm, $list, $sources, %options) = @_; - my %removables; - - #- make sure everything is correct on input... - $urpm->{media} or return; - @{$urpm->{media}} == @$list or return; - - #- examine if given medium is already inside a removable device. - my $check_notfound = sub { - my ($id, $dir, $removable) = @_; - if ($dir) { - $urpm->try_mounting($dir, $removable); - -e $dir or return 2; - } - foreach (values %{$list->[$id]}) { - chomp; - my $dir_ = file_from_local_url($_) or next; - $dir_ =~ m!/.*/! or next; #- is this really needed?? - unless ($dir) { - $dir = $dir_; - $urpm->try_mounting($dir, $removable); - } - -r $dir_ or return 1; - } - 0; - }; - #- removable media have to be examined to keep mounted the one that has - #- more packages than others. - my $examine_removable_medium = sub { - my ($id, $device) = @_; - my $medium = $urpm->{media}[$id]; - if (my $dir = file_from_local_url($medium->{url})) { - #- the directory given does not exist and may be accessible - #- by mounting some other directory. Try to figure it out and mount - #- everything that might be necessary. - while ($check_notfound->($id, $dir, is_iso($medium->{removable}) ? $medium->{removable} : 'removable')) { - is_iso($medium->{removable}) || $options{ask_for_medium} - or $urpm->{fatal}(4, N("medium \"%s\" is not selected", $medium->{name})); - $urpm->try_umounting($dir); - system("/usr/bin/eject '$device' 2>/dev/null"); - is_iso($medium->{removable}) - || $options{ask_for_medium}(remove_internal_name($medium->{name}), $medium->{removable}) - or $urpm->{fatal}(4, N("medium \"%s\" is not selected", $medium->{name})); - } - if (-e $dir) { - while (my ($i, $url) = each %{$list->[$id]}) { - chomp $url; - my ($filepath, $filename) = do { - my $f = file_from_local_url($url) or next; - $f =~ m!/.*/! or next; #- is this really needed?? - dirname($f), basename($f); - }; - if (-r $filepath) { - #- we should assume a possibly buggy removable device... - #- First, copy in partial cache, and if the package is still good, - #- transfer it to the rpms cache. - unlink "$urpm->{cachedir}/partial/$filename"; - if (urpm::util::copy($filepath, "$urpm->{cachedir}/partial") && - URPM::verify_rpm("$urpm->{cachedir}/partial/$filename", nosignatures => 1)) - { - #- now we can consider the file to be fine. - unlink "$urpm->{cachedir}/rpms/$filename"; - urpm::util::move("$urpm->{cachedir}/partial/$filename", "$urpm->{cachedir}/rpms/$filename"); - -r "$urpm->{cachedir}/rpms/$filename" and $sources->{$i} = "$urpm->{cachedir}/rpms/$filename"; - } - } - unless ($sources->{$i}) { - #- fallback to use other method for retrieving the file later. - $urpm->{error}(N("unable to read rpm file [%s] from medium \"%s\"", $filepath, $medium->{name})); - } - } - } else { - $urpm->{error}(N("medium \"%s\" is not selected", $medium->{name})); - } - } else { - #- we have a removable device that is not removable, well... - $urpm->{error}(N("inconsistent medium \"%s\" marked removable but not really", $medium->{name})); - } - }; - - foreach (0..$#$list) { - values %{$list->[$_]} or next; - my $medium = $urpm->{media}[$_]; - #- examine non removable device but that may be mounted. - if ($medium->{removable}) { - push @{$removables{$medium->{removable}} ||= []}, $_; - } elsif (my $dir = file_from_local_url($medium->{url})) { - -e $dir || $urpm->try_mounting($dir) or - $urpm->{error}(N("unable to access medium \"%s\"", $medium->{name})), next; - } - } - foreach my $device (keys %removables) { - next if $device =~ m![^a-zA-Z0-9_./-]!; #- bad path - #- Here we have only removable devices. - #- If more than one media uses this device, we have to sort - #- needed packages to copy the needed rpm files. - if (@{$removables{$device}} > 1) { - my @sorted_media = sort { values(%{$list->[$a]}) <=> values(%{$list->[$b]}) } @{$removables{$device}}; - - #- check if a removable device is already mounted (and files present). - if (my ($already_mounted_medium) = grep { !$check_notfound->($_) } @sorted_media) { - @sorted_media = grep { $_ ne $already_mounted_medium } @sorted_media; - unshift @sorted_media, $already_mounted_medium; - } - - #- mount all except the biggest one. - my $biggest = pop @sorted_media; - foreach (@sorted_media) { - $examine_removable_medium->($_, $device); - } - #- now mount the last one... - $removables{$device} = [ $biggest ]; - } - - $examine_removable_medium->($removables{$device}[0], $device); - } - - 1; -} - -# TODO verify that files are downloaded from the right corresponding media -sub download_packages_of_distant_media { - my ($urpm, $list, $sources, $error_sources, %options) = @_; - - #- get back all ftp and http accessible rpm files into the local cache - foreach my $n (0..$#$list) { - my %distant_sources; - - #- ignore media that contain nothing for the current set of files - values %{$list->[$n]} or next; - - #- examine all files to know what can be indexed on multiple media. - while (my ($i, $url) = each %{$list->[$n]}) { - #- the given URL is trusted, so the file can safely be ignored. - defined $sources->{$i} and next; - my $local_file = file_from_local_url($url); - if ($local_file && $local_file =~ /\.rpm$/) { - if (-r $local_file) { - $sources->{$i} = $local_file; - } else { - $error_sources->{$i} = $local_file; - } - } elsif ($url =~ m!^([^:]*):/(.*/([^/]*\.rpm))\Z!) { - $distant_sources{$i} = "$1:/$2"; #- will download now - } else { - $urpm->{error}(N("malformed URL: [%s]", $url)); - } - } - - #- download files from the current medium. - if (%distant_sources) { - eval { - $urpm->{log}(N("retrieving rpm files from medium \"%s\"...", $urpm->{media}[$n]{name})); - sync_webfetch($urpm, $urpm->{media}[$n], [ values %distant_sources ], - \%options, resume => $options{resume}, callback => $options{callback}); - $urpm->{log}(N("...retrieving done")); - }; - $@ and $urpm->{error}(N("...retrieving failed: %s", $@)); - #- clean files that have not been downloaded, but keep in mind - #- there have been problems downloading them at least once, this - #- is necessary to keep track of failing downloads in order to - #- present the error to the user. - foreach my $i (keys %distant_sources) { - my ($filename) = $distant_sources{$i} =~ m|/([^/]*\.rpm)$|; - if ($filename && -s "$urpm->{cachedir}/partial/$filename" && - URPM::verify_rpm("$urpm->{cachedir}/partial/$filename", nosignatures => 1)) - { - #- it seems the the file has been downloaded correctly and has been checked to be valid. - unlink "$urpm->{cachedir}/rpms/$filename"; - urpm::util::move("$urpm->{cachedir}/partial/$filename", "$urpm->{cachedir}/rpms/$filename"); - -r "$urpm->{cachedir}/rpms/$filename" and $sources->{$i} = "$urpm->{cachedir}/rpms/$filename"; - } - unless ($sources->{$i}) { - $error_sources->{$i} = $distant_sources{$i}; - } - } - } - } - - #- clean failed download which have succeeded. - delete @$error_sources{keys %$sources}; - - 1; -} - -#- prepare transaction. -sub prepare_transaction { - my ($_urpm, $set, $list, $sources, $transaction_list, $transaction_sources) = @_; - - foreach my $id (@{$set->{upgrade}}) { - foreach (0..$#$list) { - exists $list->[$_]{$id} and $transaction_list->[$_]{$id} = $list->[$_]{$id}; - } - exists $sources->{$id} and $transaction_sources->{$id} = $sources->{$id}; - } -} - #- extract package that should be installed instead of upgraded, +#- installing instead of upgrading is useful +#- - for inst.list (cf flag disable_obsolete) #- sources is a hash of id -> source rpm filename. sub extract_packages_to_install { my ($urpm, $sources, $state) = @_; my %inst; - my $rej = ref $state ? $state->{rejected} || {} : {}; foreach (keys %$sources) { my $pkg = $urpm->{depslist}[$_] or next; - $pkg->flag_disable_obsolete || !$pkg->flag_installed - and !grep { exists $rej->{$_}{closure}{$pkg->fullname} } keys %$rej - and $inst{$pkg->id} = delete $sources->{$pkg->id}; + $pkg->flag_disable_obsolete + and $inst{$pkg->id} = delete $sources->{$pkg->id}; } \%inst; } -# size of the installation progress bar -my $progress_size = 45; -eval { - require Term::ReadKey; - ($progress_size) = Term::ReadKey::GetTerminalSize(); - $progress_size -= 35; - $progress_size < 5 and $progress_size = 5; -}; - -# install logger callback -sub install_logger { - my ($urpm, $type, $id, $subtype, $amount, $total) = @_; - my $pkg = defined $id && $urpm->{depslist}[$id]; - my $total_pkg = $urpm->{nb_install}; - local $| = 1; - - if ($subtype eq 'start') { - $urpm->{logger_progress} = 0; - if ($type eq 'trans') { - $urpm->{logger_id} ||= 0; - $urpm->{logger_count} ||= 0; - my $p = N("Preparing..."); - print $p, " " x (33 - length $p); - } else { - ++$urpm->{logger_id}; - my $pname = $pkg ? $pkg->name : ''; - ++$urpm->{logger_count} if $pname; - my $cnt = $pname ? $urpm->{logger_count} : '-'; - $pname ||= N("[repackaging]"); - printf "%9s: %-22s", $cnt . "/" . $total_pkg, $pname; - } - } elsif ($subtype eq 'stop') { - if ($urpm->{logger_progress} < $progress_size) { - print '#' x ($progress_size - $urpm->{logger_progress}), "\n"; - $urpm->{logger_progress} = 0; - } - } elsif ($subtype eq 'progress') { - my $new_progress = $total > 0 ? int($progress_size * $amount / $total) : $progress_size; - if ($new_progress > $urpm->{logger_progress}) { - print '#' x ($new_progress - $urpm->{logger_progress}); - $urpm->{logger_progress} = $new_progress; - $urpm->{logger_progress} == $progress_size and print "\n"; - } - } -} - -#- install packages according to each hash (remove, install or upgrade). -sub install { - my ($urpm, $remove, $install, $upgrade, %options) = @_; - my %readmes; - - #- allow process to be forked now. - my $pid; - my ($CHILD_RETURNS, $ERROR_OUTPUT); - if ($options{fork}) { - pipe($CHILD_RETURNS, $ERROR_OUTPUT); - defined($pid = fork()) or die "Can't fork: $!\n"; - if ($pid) { - # parent process - close $ERROR_OUTPUT; - - $urpm->{log}(N("using process %d for executing transaction", $pid)); - #- now get all errors from the child and return them directly. - my @l; - local $_; - while (<$CHILD_RETURNS>) { - chomp; - if (/^::logger_id:(\d*):(\d*)/) { - $urpm->{logger_id} = $1; - $urpm->{logger_count} = $2 if $2; - } else { - push @l, $_; - } - } - - close $CHILD_RETURNS; - waitpid($pid, 0); - #- take care of return code from transaction, an error should be returned directly. - $? >> 8 and exit $? >> 8; - - return @l; - } else { - # child process - close $CHILD_RETURNS; - } - } - #- beware this can be a child process or the main process now... - - my $db = db_open_or_die($urpm, $urpm->{root}, !$options{test}); #- open in read/write mode unless testing installation. - - my $trans = $db->create_transaction($urpm->{root}); - if ($trans) { - sys_log("transaction on %s (remove=%d, install=%d, upgrade=%d)", $urpm->{root} || '/', scalar(@{$remove || []}), scalar(values %$install), scalar(values %$upgrade)); - $urpm->{log}(N("created transaction for installing on %s (remove=%d, install=%d, upgrade=%d)", $urpm->{root} || '/', - scalar(@{$remove || []}), scalar(values %$install), scalar(values %$upgrade))); - } else { - return N("unable to create transaction"); - } - - my ($update, @l) = 0; - my @produced_deltas; - - foreach (@$remove) { - if ($trans->remove($_)) { - $urpm->{log}(N("removing package %s", $_)); - } else { - $urpm->{error}(N("unable to remove package %s", $_)); - } - } - foreach my $mode ($install, $upgrade) { - foreach (keys %$mode) { - my $pkg = $urpm->{depslist}[$_]; - $pkg->update_header($mode->{$_}); - if ($pkg->payload_format eq 'drpm') { #- handle deltarpms - my $true_rpm = urpm::sys::apply_delta_rpm($mode->{$_}, "$urpm->{cachedir}/rpms", $pkg); - if ($true_rpm) { - push @produced_deltas, ($mode->{$_} = $true_rpm); #- fix path - } else { - $urpm->{error}(N("unable to extract rpm from delta-rpm package %s", $mode->{$_})); - } - } - if ($trans->add($pkg, update => $update, - $options{excludepath} ? (excludepath => [ split /,/, $options{excludepath} ]) : () - )) { - $urpm->{log}(N("adding package %s (id=%d, eid=%d, update=%d, file=%s)", scalar($pkg->fullname), - $_, $pkg->id, $update, $mode->{$_})); - } else { - $urpm->{error}(N("unable to install package %s", $mode->{$_})); - } - } - ++$update; - } - if (($options{nodeps} || !(@l = $trans->check(%options))) && ($options{noorder} || !(@l = $trans->order))) { - my $fh; - #- assume default value for some parameter. - $options{delta} ||= 1000; - $options{callback_open} ||= sub { - my ($_data, $_type, $id) = @_; - $fh = $urpm->open_safe('<', $install->{$id} || $upgrade->{$id}); - $fh ? fileno $fh : undef; - }; - $options{callback_close} ||= sub { - my ($urpm, undef, $pkgid) = @_; - return unless defined $pkgid; - my $pkg = $urpm->{depslist}[$pkgid]; - my $fullname = $pkg->fullname; - my $trtype = (grep { /\Q$fullname\E/ } values %$install) ? 'install' : '(upgrade|update)'; - foreach ($pkg->files) { /\bREADME(\.$trtype)?\.urpmi$/ and $readmes{$_} = $fullname } - close $fh if defined $fh; - }; - if ($::verbose >= 0 && (scalar keys %$install || scalar keys %$upgrade)) { - $options{callback_inst} ||= \&install_logger; - $options{callback_trans} ||= \&install_logger; - } - @l = $trans->run($urpm, %options); - - #- don't clear cache if transaction failed. We might want to retry. - if (@l == 0 && !$options{test} && $options{post_clean_cache}) { - #- examine the local cache to delete packages which were part of this transaction - foreach (keys %$install, keys %$upgrade) { - my $pkg = $urpm->{depslist}[$_]; - unlink "$urpm->{cachedir}/rpms/" . $pkg->filename; - } - } - } - unlink @produced_deltas; - - #- now exit or return according to current status. - if (defined $pid && !$pid) { #- child process - print $ERROR_OUTPUT "::logger_id:$urpm->{logger_id}:$urpm->{logger_count}\n"; #- allow main urpmi to know transaction numbering... - print $ERROR_OUTPUT "$_\n" foreach @l; - close $ERROR_OUTPUT; - #- keep safe exit now (with destructor call). - exit 0; - } else { - #- when non-forking - # !!! BUG: this part of the code is not called when forking !!! - # !!! BUG: when forking %readmes is empty, since the child is not passing this information to its parent !!! - if ($::verbose >= 0 && keys %readmes) { - foreach (keys %readmes) { - print "-" x 70, "\n", N("More information on package %s", $readmes{$_}), "\n"; - my $fh; open $fh, '<', $_ and do { - print while <$fh>; - close $fh; - }; - print "-" x 70, "\n"; - } - } - return @l; - } -} - -#- install all files to node as remembered according to resolving done. -sub parallel_install { - my @para = @_; - my ($urpm, $_remove, $_install, $_upgrade, %_options) = @para; - $urpm->{parallel_handler}->parallel_install(@para); -} - -#- find packages to remove. -#- options: -#- bundle -#- callback_base -#- callback_fuzzy -#- callback_notfound -#- force -#- matches -#- root -#- test -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 = db_open_or_die($urpm, $options{root}); - my (@m, @notfound); - - if (!$options{matches}) { - foreach (@$l) { - my ($n, $found); - - #- check if name-version-release-architecture was given. - if (($n) = /^(.*)-[^\-]*-[^\-]*\.[^\.\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - $p->fullname eq $_ or return; - $urpm->resolve_rejected($db, $state, $p, removed => 1, bundle => $options{bundle}); - push @m, scalar $p->fullname; - $found = 1; - }); - $found and next; - } - - #- check if name-version-release was given. - if (($n) = /^(.*)-[^\-]*-[^\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - my ($name, $version, $release) = $p->fullname; - "$name-$version-$release" eq $_ or return; - $urpm->resolve_rejected($db, $state, $p, removed => 1, bundle => $options{bundle}); - push @m, scalar $p->fullname; - $found = 1; - }); - $found and next; - } - - #- check if name-version was given. - if (($n) = /^(.*)-[^\-]*$/) { - $db->traverse_tag('name', [ $n ], sub { - my ($p) = @_; - my ($name, $version) = $p->fullname; - "$name-$version" eq $_ or return; - $urpm->resolve_rejected($db, $state, $p, removed => 1, bundle => $options{bundle}); - push @m, scalar $p->fullname; - $found = 1; - }); - $found and next; - } - - #- check if only name was given. - $db->traverse_tag('name', [ $_ ], sub { - my ($p) = @_; - $p->name eq $_ or return; - $urpm->resolve_rejected($db, $state, $p, removed => 1, bundle => $options{bundle}); - push @m, scalar $p->fullname; - $found = 1; - }); - $found and next; - - push @notfound, $_; - } - if (!$options{force} && @notfound && @$l > 1) { - $options{callback_notfound} && $options{callback_notfound}->($urpm, @notfound) - or return (); - } - } - if ($options{matches} || @notfound) { - my $match = join "|", map { quotemeta } @$l; - my $qmatch = qr/$match/; - - #- reset what has been already found. - %$state = (); - @m = (); - - #- search for packages that match, and perform closure again. - $db->traverse(sub { - my ($p) = @_; - my $f = scalar $p->fullname; - $f =~ $qmatch or return; - $urpm->resolve_rejected($db, $state, $p, removed => 1, bundle => $options{bundle}); - push @m, $f; - }); - - if (!$options{force} && @notfound) { - if (@m) { - $options{callback_fuzzy} && $options{callback_fuzzy}->($urpm, @$l > 1 ? $match : $l->[0], @m) - or return (); - } else { - $options{callback_notfound} && $options{callback_notfound}->($urpm, @notfound) - or return (); - } - } - } - - #- check if something needs to be removed. - find_removed_from_basesystem($urpm, $db, $state, $options{callback_base}) - or return (); - } - grep { $state->{rejected}{$_}{removed} && !$state->{rejected}{$_}{obsoleted} } keys %{$state->{rejected}}; -} - -sub find_removed_from_basesystem { - my ($urpm, $db, $state, $callback_base) = @_; - if ($callback_base && %{$state->{rejected} || {}}) { - my %basepackages; - my @dont_remove = ('basesystem', split /,\s*/, $urpm->{global_config}{'prohibit-remove'}); - #- check if a package to be removed is a part of basesystem requires. - $db->traverse_tag('whatprovides', \@dont_remove, sub { - my ($p) = @_; - $basepackages{$p->fullname} = 0; - }); - foreach (grep { $state->{rejected}{$_}{removed} && !$state->{rejected}{$_}{obsoleted} } keys %{$state->{rejected}}) { - exists $basepackages{$_} or next; - ++$basepackages{$_}; - } - if (grep { $_ } values %basepackages) { - return $callback_base->($urpm, grep { $basepackages{$_} } keys %basepackages); - } - } - return 1; -} - -#- 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 remove fatal error") }; - $urpm->{parallel_handler}->parallel_find_remove($urpm, $state, $remove, %options, - callback_notfound => undef, - callback_fuzzy => $callback, - callback_base => $callback, - ); -} - -#- misc functions to help finding ask_unselect and ask_remove elements with their reasons translated. -sub unselected_packages { - my (undef, $state) = @_; - grep { $state->{rejected}{$_}{backtrack} } keys %{$state->{rejected} || {}}; -} - -sub uniq { my %l; $l{$_} = 1 foreach @_; grep { delete $l{$_} } @_ } - -sub translate_why_unselected { - my ($urpm, $state, @fullnames) = @_; - - join("\n", map { translate_why_unselected_one($urpm, $state, $_) } sort @fullnames); -} - -sub translate_why_unselected_one { - my ($urpm, $state, $fullname) = @_; - - my $rb = $state->{rejected}{$fullname}{backtrack}; - my @froms = keys %{$rb->{closure} || {}}; - my @unsatisfied = @{$rb->{unsatisfied} || []}; - my $s = join ", ", ( - (map { N("due to missing %s", $_) } @froms), - (map { N("due to unsatisfied %s", $_) } uniq(map { - #- XXX in theory we shouldn't need this, dependencies (and not ids) should - #- already be present in @unsatisfied. But with biarch packages this is - #- not always the case. - /\D/ ? $_ : scalar($urpm->{depslist}[$_]->fullname); - } @unsatisfied)), - $rb->{promote} && !$rb->{keep} ? N("trying to promote %s", join(", ", @{$rb->{promote}})) : (), - $rb->{keep} ? N("in order to keep %s", join(", ", @{$rb->{keep}})) : (), - ); - $fullname . ($s ? " ($s)" : ''); -} - -sub removed_packages { - my (undef, $state) = @_; - grep { - $state->{rejected}{$_}{removed} && !$state->{rejected}{$_}{obsoleted}; - } keys %{$state->{rejected} || {}}; -} - -sub translate_why_removed { - my ($urpm, $state, @fullnames) = @_; - join("\n", map { translate_why_removed_one($urpm, $state, $_) } sort @fullnames); -} -sub translate_why_removed_one { - my ($urpm, $state, $fullname) = @_; - - my $closure = $state->{rejected}{$fullname}{closure}; - my ($from) = keys %$closure; - my ($whyk) = keys %{$closure->{$from}}; - my $whyv = $closure->{$from}{$whyk}; - my $frompkg = $urpm->search($from, strict_fullname => 1); - my $s = do { - if ($whyk =~ /old_requested/) { - N("in order to install %s", $frompkg ? scalar $frompkg->fullname : $from); - } elsif ($whyk =~ /unsatisfied/) { - join(",\n ", map { - if (/([^\[\s]*)(?:\[\*\])?(?:\[|\s+)([^\]]*)\]?$/ && $2 ne '*') { - N("due to unsatisfied %s", "$1 $2"); - } else { - N("due to missing %s", $_); - } - } @$whyv); - } elsif ($whyk =~ /conflicts/) { - N("due to conflicts with %s", $whyv); - } elsif ($whyk =~ /unrequested/) { - N("unrequested"); - } else { - undef; - } - }; - #- now insert the reason if available. - $fullname . ($s ? "\n ($s)" : ''); -} - -sub check_sources_signatures { - my ($urpm, $sources_install, $sources, %options) = @_; - my ($medium, %invalid_sources); - my $s = $sources_install; - - foreach my $id (keys %$sources_install, -1, keys %$sources) { - if ($id == -1) { $s = $sources; next } - my $filepath = $s->{$id}; - my $verif = URPM::verify_signature($filepath); - - if ($verif =~ /NOT OK/) { - $verif =~ s/\n//g; - $invalid_sources{$filepath} = N("Invalid signature (%s)", $verif); - } else { - unless ($medium && is_valid_medium($medium) && - $medium->{start} <= $id && $id <= $medium->{end}) - { - $medium = undef; - foreach (@{$urpm->{media}}) { - is_valid_medium($_) && $_->{start} <= $id && $id <= $_->{end} - and $medium = $_, last; - } - } - #- no medium found for this rpm ? - next if !$medium; - #- check whether verify-rpm is specifically disabled for this medium - next if defined $medium->{'verify-rpm'} && !$medium->{'verify-rpm'}; - - my $key_ids = $medium->{'key-ids'} || $urpm->{options}{'key-ids'}; - #- check that the key ids of the medium match the key ids of the package. - if ($key_ids) { - my $valid_ids = 0; - my $invalid_ids = 0; - - foreach my $key_id ($verif =~ /(?:key id \w{8}|#)(\w+)/gi) { - if (grep { hex($_) == hex($key_id) } split /[,\s]+/, $key_ids) { - ++$valid_ids; - } else { - ++$invalid_ids; - } - } - - if ($invalid_ids) { - $invalid_sources{$filepath} = N("Invalid Key ID (%s)", $verif); - } elsif (!$valid_ids) { - $invalid_sources{$filepath} = N("Missing signature (%s)", $verif); - } - } - #- invoke check signature callback. - $options{callback} and $options{callback}->( - $urpm, $filepath, %options, - id => $id, - verif => $verif, - why => $invalid_sources{$filepath}, - ); - } - } +#- deprecated +sub install { require urpm::install; &urpm::install::install } - map { ($options{basename} ? basename($_) : $_) . ($options{translate} ? ": $invalid_sources{$_}" : "") } - sort keys %invalid_sources; -} +#- deprecated +sub parallel_remove { &urpm::parallel::remove } #- get reason of update for packages to be updated #- use all update medias if none given @@ -3402,7 +233,7 @@ sub get_updates_description { @update_medias or @update_medias = grep { !$_->{ignore} && $_->{update} } @{$urpm->{media}}; - foreach (map { cat_(statedir_descriptions($urpm, $_)), '%package dummy' } @update_medias) { + foreach (map { cat_utf8(urpm::media::statedir_descriptions($urpm, $_)), '%package dummy' } @update_medias) { /^%package (.+)/ and do { if (exists $cur->{importance} && $cur->{importance} ne "security" && $cur->{importance} ne "bugfix") { $cur->{importance} = 'normal'; @@ -3423,63 +254,6 @@ sub get_updates_description { \%update_descr; } -#- parse an MD5SUM file from a mirror -sub get_md5sum { - my ($md5sum_file, $f) = @_; - my $basename = basename($f); - - my ($retrieved_md5sum) = map { - my ($md5sum, $file) = m|(\S+)\s+(?:\./)?(\S+)|; - $file && $file eq $basename ? $md5sum : @{[]}; - } cat_($md5sum_file); - - $retrieved_md5sum; -} - -sub parse_md5sum { - my ($urpm, $md5sum_file, $basename) = @_; - $urpm->{log}(N("examining MD5SUM file")); - my $retrieved_md5sum = get_md5sum($md5sum_file, $basename) - or $urpm->{log}(N("warning: md5sum for %s unavailable in MD5SUM file", $basename)); - return $retrieved_md5sum; -} - -sub recompute_local_md5sum { - my ($urpm, $medium, $force) = @_; - if ($force) { - #- force downloading the file again, else why a force option has been defined ? - delete $medium->{md5sum}; - } else { - compute_local_md5sum($urpm, $medium) if !$medium->{md5sum}; - } -} - -sub compute_local_md5sum { - my ($urpm, $medium) = @_; - - $urpm->{log}(N("computing md5sum of existing source hdlist (or synthesis)")); - my $f = statedir_hdlist_or_synthesis($urpm, $medium); - if (-e $f) { - $medium->{md5sum} = md5sum($f); - } -} - -sub syserror { my ($urpm, $msg, $info) = @_; $urpm->{error}("$msg [$info] [$!]") } - -sub open_safe { - my ($urpm, $sense, $filename) = @_; - open my $f, $sense, $filename - or $urpm->syserror($sense eq '>' ? "Can't write file" : "Can't open file", $filename), return undef; - return $f; -} - -sub opendir_safe { - my ($urpm, $dirname) = @_; - opendir my $d, $dirname - or $urpm->syserror("Can't open directory", $dirname), return undef; - return $d; -} - sub error_restricted ($) { my ($urpm) = @_; $urpm->{fatal}(2, N("This operation is forbidden while running in restricted mode")); |