File Coverage

blib/lib/urpm.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package urpm;
2              
3              
4 1     1   6 no warnings 'utf8';
  1         2  
  1         35  
5 1     1   6 use strict;
  1         1  
  1         19  
6 1     1   4 use File::Find ();
  1         2  
  1         11  
7 1     1   33 use urpm::msg;
  0            
  0            
8             use urpm::download;
9             use urpm::util qw(basename begins_with cat_ cat_utf8 dirname file2absolute_file member);
10             use urpm::sys;
11             use urpm::cfg;
12             use urpm::md5sum;
13             # perl_checker: require urpm::args
14             # perl_checker: require urpm::media
15             # perl_checker: require urpm::parallel
16              
17             our $VERSION = '8.111';
18             our @ISA = qw(URPM Exporter);
19             our @EXPORT_OK = ('file_from_local_url', 'file_from_local_medium', 'is_local_medium');
20              
21             # Prepare exit code. If you change this, the exiting with a failure and the message given will be postponed to the end of the overall processing.
22             our $postponed_msg = N("While some packages may have been installed, there were failures.\n");
23             our $postponed_code = 0;
24              
25             use URPM;
26             use URPM::Resolve;
27              
28              
29             =head1 NAME
30              
31             urpm - Mageia perl tools to handle the urpmi database
32              
33             =head1 DESCRIPTION
34              
35             C is used by urpmi executables to manipulate packages and media
36             on a Mageia Linux distribution.
37              
38             =head2 The urpm class
39              
40             =over 4
41              
42             =cut
43              
44             #- this violently overrides is_arch_compat() to always return true.
45             sub shunt_ignorearch {
46             eval q( sub URPM::Package::is_arch_compat { 1 } );
47             }
48              
49             sub xml_info_policies() { qw(never on-demand update-only always) }
50              
51             sub default_options {
52             {
53             'split-level' => 1,
54             'split-length' => 50,
55             'verify-rpm' => 1,
56             'post-clean' => 1,
57             'xml-info' => 'on-demand',
58             'max-round-robin-tries' => 5,
59             'max-round-robin-probes' => 2,
60             'days-between-mirrorlist-update' => 5,
61             'nb-of-new-unrequested-pkgs-between-auto-select-orphans-check' => 10,
62             };
63             }
64              
65             =item urpm->new()
66              
67             The constructor creates a new urpm object. It's a blessed hash that
68             contains fields from L, and also the following fields:
69              
70             B: { id => src_rpm_file|spec_file }
71              
72             B: [ {
73             start => int, end => int, name => string, url => string,
74             virtual => bool, media_info_dir => string, with_synthesis => string,
75             no-media-info => bool,
76             iso => string, downloader => string,
77             ignore => bool, update => bool, modified => bool, really_modified => bool,
78             unknown_media_info => bool,
79             } ],
80              
81             B: hashref of urpm options
82              
83             several paths:
84              
85             =over
86              
87             B: path of urpmi.cfg (/etc/urpmi/urpmi.cfg)
88              
89             B: path of mediacfg.d (/etc/urpmi/mediacfg.d)
90              
91             B: path of skip.list (/etc/urpmi/skip.list),
92              
93             B: path of inst.list (/etc/urpmi/inst.list),
94              
95             B: path of prefer.list (/etc/urpmi/prefer.list),
96              
97             B: path of prefer.vendor.list (/etc/urpmi/prefer.vendor.list),
98              
99             B: path of netrc (/etc/urpmi/netrc),
100              
101             B: state directory (/var/lib/urpmi),
102              
103             B: cache directory (/var/cache/urpmi),
104              
105             B: path of the rooted system (when using global urpmi config),
106              
107             B: path of the rooted system (when both urpmi & rpmdb are chrooted)
108              
109             =back
110              
111             Several subs:
112              
113             =over
114              
115             B: sub for relaying fatal errors (should popup in GUIes)
116              
117             B: sub for relaying other errors
118              
119             B: sub for relaying messages if --verbose
120              
121             B: sub for always displayed messages, enable to redirect output for eg: installer
122              
123             B: sub for messages displayed unless --quiet
124              
125             =back
126              
127             All C methods are available on an urpm object.
128              
129             =cut
130              
131             sub new {
132             my ($class) = @_;
133             my $self;
134             $self = bless {
135             # from URPM
136             depslist => [],
137             provides => {},
138             obsoletes => {},
139              
140             media => undef,
141             options => {},
142              
143             fatal => sub { printf STDERR "%s\n", $_[1]; exit($_[0]) },
144             error => sub { printf STDERR "%s\n", $_[0] },
145             info => sub { printf "%s\n", $_[0] }, #- displayed unless --quiet
146             log => sub { printf "%s\n", $_[0] }, #- displayed if --verbose
147             print => sub { printf "%s\n", $_[0] }, #- always displayed, enable to redirect output for eg: installer
148             }, $class;
149              
150             set_files($self, '');
151             $self->set_nofatal(1);
152             $self;
153             }
154              
155             =item urpm->new_parse_cmdline()
156              
157             Like urpm->new but also parse the command line and parse the configuration file.
158              
159             =cut
160              
161             sub new_parse_cmdline {
162             my ($class) = @_;
163             my $urpm = $class->new;
164             urpm::args::parse_cmdline(urpm => $urpm);
165             get_global_options($urpm);
166             $urpm;
167             }
168              
169             sub _add2hash { my ($a, $b) = @_; while (my ($k, $v) = each %{$b || {}}) { defined $a->{$k} or $a->{$k} = $v } $a }
170              
171             sub get_global_options {
172             my ($urpm) = @_;
173              
174             my $config = urpm::cfg::load_config($urpm->{config})
175             or $urpm->{fatal}(6, $urpm::cfg::err);
176              
177             if (my $global = $config->{global}) {
178             _add2hash($urpm->{options}, $global);
179             }
180             #- remember global options for write_config
181             $urpm->{global_config} = $config->{global};
182              
183             _add2hash($urpm->{options}, default_options());
184             }
185              
186             sub prefer_rooted {
187             my ($root, $file) = @_;
188             -e "$root$file" ? "$root$file" : $file;
189             }
190              
191             sub check_dir {
192             my ($urpm, $dir) = @_;
193             -d $dir && ! -l $dir or $urpm->{fatal}(1, N("fail to create directory %s", $dir));
194             -o $dir && -w $dir or $urpm->{fatal}(1, N("invalid owner for directory %s", $dir));
195             }
196              
197             sub init_dir {
198             my ($urpm, $dir) = @_;
199              
200             mkdir $dir, 0755; # try to create it
201              
202             check_dir($urpm, $dir);
203              
204             mkdir "$dir/partial";
205             mkdir "$dir/rpms";
206              
207             $dir;
208             }
209              
210             sub userdir_prefix {
211             my ($_urpm) = @_;
212             '/tmp/.urpmi-';
213             }
214              
215             sub valid_statedir {
216             my ($urpm) = @_;
217             $< or return;
218              
219             my $dir = ($urpm->{urpmi_root} || '') . userdir_prefix($urpm) . $< . "/lib";
220             init_dir($urpm, $dir);
221             }
222              
223             sub userdir {
224             #mdkonline uses userdir because it runs as user
225             my ($urpm) = @_;
226             $< or return;
227              
228             my $dir = ($urpm->{urpmi_root} || '') . userdir_prefix($urpm) . $<;
229             init_dir($urpm, $dir);
230             }
231              
232             sub ensure_valid_cachedir {
233             my ($urpm) = @_;
234             if (my $dir = userdir($urpm)) {
235             $urpm->{cachedir} = $dir;
236             }
237             -w "$urpm->{cachedir}/partial" or $urpm->{fatal}(1, N("Can not download packages into %s", "$urpm->{cachedir}/partial"));
238             }
239              
240             sub valid_cachedir {
241             my ($urpm) = @_;
242             userdir($urpm) || $urpm->{cachedir};
243             }
244              
245             sub is_temporary_file {
246             my ($urpm, $f) = @_;
247              
248             begins_with($f, $urpm->{cachedir});
249             }
250              
251             sub set_env {
252             my ($urpm, $env) = @_;
253             -d $env or $urpm->{fatal}(8, N("Environment directory %s does not exist", $env));
254             print N("using specific environment on %s\n", $env);
255             #- setting new environment.
256             $urpm->{config} = "$env/urpmi.cfg";
257             if (cat_($urpm->{config}) =~ /^\s*virtual\s*$/m) {
258             print "dropping virtual from $urpm->{config}\n";
259             system(q(perl -pi -e 's/^\s*virtual\s*$//' ) . $urpm->{config});
260             }
261             $urpm->{mediacfgdir} = "$env/mediacfg.d";
262             $urpm->{skiplist} = "$env/skip.list";
263             $urpm->{instlist} = "$env/inst.list";
264             $urpm->{prefer_list} = "$env/prefer.list";
265             $urpm->{prefer_vendor_list} = "$env/prefer.vendor.list";
266             $urpm->{statedir} = $env;
267             $urpm->{env_rpmdb} = "$env/rpmdb.cz";
268             $urpm->{env_dir} = $env;
269             }
270              
271             sub set_files {
272             my ($urpm, $urpmi_root) = @_;
273              
274             $urpmi_root and $urpmi_root = file2absolute_file($urpmi_root);
275              
276             my %h = (
277             config => "$urpmi_root/etc/urpmi/urpmi.cfg",
278             mediacfgdir => "$urpmi_root/etc/urpmi/mediacfg.d",
279             skiplist => prefer_rooted($urpmi_root, '/etc/urpmi/skip.list'),
280             instlist => prefer_rooted($urpmi_root, '/etc/urpmi/inst.list'),
281             prefer_list => prefer_rooted($urpmi_root, '/etc/urpmi/prefer.list'),
282             prefer_vendor_list =>
283             prefer_rooted($urpmi_root, '/etc/urpmi/prefer.vendor.list'),
284             private_netrc => "$urpmi_root/etc/urpmi/netrc",
285             statedir => "$urpmi_root/var/lib/urpmi",
286             cachedir => "$urpmi_root/var/cache/urpmi",
287             root => $urpmi_root,
288             $urpmi_root ? (urpmi_root => $urpmi_root) : @{[]},
289             );
290             $urpm->{$_} = $h{$_} foreach keys %h;
291              
292             create_var_lib_rpm($urpm, %h);
293              
294             # policy is too use chroot environment only for --urpmi-root, not for --root:
295             if ($urpmi_root && -e "$urpmi_root/etc/rpm/macros") {
296             URPM::loadmacrosfile("$urpmi_root/etc/rpm/macros");
297             }
298             }
299              
300             sub create_var_lib_rpm {
301             my ($urpm, %h) = @_;
302             require File::Path;
303             File::Path::mkpath([ $h{statedir},
304             (map { "$h{cachedir}/$_" } qw(partial rpms)),
305             dirname($h{config}),
306             "$urpm->{root}/var/lib/rpm",
307             "$urpm->{root}/var/tmp",
308             ]);
309             }
310              
311             sub modify_rpm_macro {
312             my ($name, $to_remove, $to_add) = @_;
313              
314             my $val = URPM::expand('%' . $name);
315             $val =~ s/$to_remove/$to_add/ or $val = join(' ', grep { $_ } $val, $to_add);
316             URPM::add_macro("$name $val");
317             }
318              
319             sub set_tune_rpm {
320             my ($urpm, $para) = @_;
321              
322             my %h = map { $_ => 1 } map {
323             if ($_ eq 'all') {
324             ('nofsync', 'private');
325             } else {
326             $_;
327             }
328             } split(',', $para);
329              
330             $urpm->{tune_rpm} = \%h;
331             }
332              
333             sub tune_rpm {
334             my ($urpm) = @_;
335              
336             if ($urpm->{tune_rpm}{nofsync}) {
337             modify_rpm_macro('__dbi_other', 'fsync', 'nofsync');
338             }
339             if ($urpm->{tune_rpm}{private}) {
340             urpm::sys::clean_rpmdb_shared_regions($urpm->{root});
341             modify_rpm_macro('__dbi_other', 'usedbenv', 'private');
342             }
343             }
344              
345             sub _blist_pkg_to_urls {
346             my ($blist, @pkgs) = @_;
347             my $base_url = $blist->{medium}{url} . '/';
348             map { $base_url . $_->filename } @pkgs;
349             }
350             sub blist_pkg_to_url {
351             my ($blist, $pkg) = @_;
352             my ($url) = _blist_pkg_to_urls($blist, $pkg);
353             $url;
354             }
355             sub blist_to_urls {
356             my ($blist) = @_;
357             _blist_pkg_to_urls($blist, values %{$blist->{pkgs}});
358             }
359             sub blist_to_filenames {
360             my ($blist) = @_;
361             map { $_->filename } values %{$blist->{pkgs}};
362             }
363              
364             sub protocol_from_url {
365             my ($url) = @_;
366             $url =~ m!^(\w+)(_[^:]*)?:! && $1;
367             }
368             sub file_from_local_url {
369             my ($url) = @_;
370             $url =~ m!^(?:removable[^:]*:/|file:/)?(/.*)! && $1;
371             }
372             sub file_from_local_medium {
373             my ($medium, $o_url) = @_;
374             my $url = $o_url || $medium->{url};
375             if ($url =~ m!^cdrom://(.*)!) {
376             my $rel = $1;
377             $medium->{mntpoint} or do { require Carp; Carp::confess("cdrom is not mounted yet!\n") };
378             "$medium->{mntpoint}/$rel";
379             } else {
380             file_from_local_url($url);
381             }
382             }
383             sub is_local_url {
384             my ($url) = @_;
385             file_from_local_url($url) || is_cdrom_url($url);
386             }
387             sub is_local_medium {
388             my ($medium) = @_;
389             is_local_url($medium->{url});
390             }
391             sub is_cdrom_url {
392             my ($url) = @_;
393             protocol_from_url($url) eq 'cdrom';
394             }
395              
396             =item db_open_or_die($urpm, $b_write_perm)
397              
398             Open RPM database (RW or not) and die if it fails
399              
400             =cut
401              
402             sub db_open_or_die_ {
403             my ($urpm, $b_write_perm) = @_;
404             my $db;
405             if ($urpm->{env_rpmdb}) {
406             #- URPM has same methods as URPM::DB and empty URPM will be seen as empty URPM::DB.
407             $db = URPM->new;
408             $db->parse_synthesis($urpm->{env_rpmdb});
409             } else {
410             $db = db_open_or_die($urpm, $urpm->{root}, $b_write_perm);
411             }
412             $db;
413             }
414              
415             # please use higher level function db_open_or_die_()
416             sub db_open_or_die {
417             my ($urpm, $root, $b_write_perm) = @_;
418              
419             $urpm->{debug} and $urpm->{debug}("opening rpmdb (root=$root, write=$b_write_perm)");
420              
421             my $db = URPM::DB::open($root, $b_write_perm || 0)
422             or $urpm->{fatal}(9, N("unable to open rpmdb"));
423              
424             $db;
425             }
426              
427             =item register_rpms($urpm, @files)
428              
429             Register local packages for being installed, keep track of source.
430              
431             =cut
432              
433             sub register_rpms {
434             my ($urpm, @files) = @_;
435             my ($start, $id, $error, %requested);
436              
437             #- examine each rpm and build the depslist for them using current
438             #- depslist and provides environment.
439             $start = @{$urpm->{depslist}};
440             foreach (@files) {
441             /\.(?:rpm|spec)$/ or $error = 1, $urpm->{error}(N("invalid rpm file name [%s]", $_)), next;
442              
443             #- if that's an URL, download.
444             if (protocol_from_url($_)) {
445             my $basename = basename($_);
446             unlink "$urpm->{cachedir}/partial/$basename";
447             $urpm->{log}(N("retrieving rpm file [%s] ...", $_));
448             if (urpm::download::sync_url($urpm, $_, quiet => 1)) {
449             $urpm->{log}(N("...retrieving done"));
450             $_ = "$urpm->{cachedir}/partial/$basename";
451             } else {
452             $urpm->{error}(N("...retrieving failed: %s", $@));
453             unlink "$urpm->{cachedir}/partial/$basename";
454             next;
455             }
456             } else {
457             -r $_ or $error = 1, $urpm->{error}(N("unable to access rpm file [%s]", $_)), next;
458             }
459              
460             if (/\.spec$/) {
461             my $pkg = URPM::spec2srcheader($_)
462             or $error = 1, $urpm->{error}(N("unable to parse spec file %s [%s]", $_, $!)), next;
463             $id = @{$urpm->{depslist}};
464             $urpm->{depslist}[$id] = $pkg;
465             $pkg->set_id($id); #- sets internal id to the depslist id.
466             $urpm->{source}{$id} = $_;
467             } else {
468             ($id) = $urpm->parse_rpm($_);
469             my $pkg = defined $id && $urpm->{depslist}[$id];
470             $pkg or $error = 1, $urpm->{error}(N("unable to register rpm file")), next;
471             $pkg->arch eq 'src' || $pkg->is_arch_compat
472             or $error = 1, $urpm->{error}(N("Incompatible architecture for rpm [%s]", $_)), next;
473             $urpm->{source}{$id} = $_;
474             }
475             }
476             $error and $urpm->{fatal}(2, N("error registering local packages"));
477             defined $id && $start <= $id and @requested{($start .. $id)} = (1) x ($id-$start+1);
478              
479             #- distribute local packages to distant nodes directly in cache of each machine.
480             if (@files && $urpm->{parallel_handler}) {
481             $urpm->{parallel_handler}->parallel_register_rpms($urpm, @files);
482             }
483              
484             %requested;
485             }
486              
487             =item is_delta_installable($urpm, $pkg, $root)
488              
489             checks whether the delta RPM represented by $pkg is installable wrt the
490             RPM DB on $root. For this, it extracts the rpm version to which the
491             delta applies from the delta rpm filename itself. So naming conventions
492             do matter :)
493              
494             =cut
495              
496             sub is_delta_installable {
497             my ($urpm, $pkg, $root) = @_;
498             $pkg->flag_installed or return 0;
499             my $f = $pkg->filename;
500             my $n = $pkg->name;
501             my ($v_match) = $f =~ /^\Q$n\E-(.*)_.+\.delta\.rpm$/;
502             my $db = db_open_or_die($urpm, $root);
503             my $v_installed;
504             $db->traverse(sub {
505             my ($p) = @_;
506             $p->name eq $n and $v_installed = $p->version . '-' . $p->release;
507             });
508             $v_match eq $v_installed;
509             }
510              
511              
512             =item extract_packages_to_install($urpm, $sources)
513              
514             Extract package that should be installed instead of upgraded,
515             installing instead of upgrading is useful
516             - for inst.list (cf flag disable_obsolete)
517              
518             Sources is a hash of id -> source rpm filename.
519              
520             =cut
521              
522             sub extract_packages_to_install {
523             my ($urpm, $sources) = @_;
524             my %inst;
525              
526             foreach (keys %$sources) {
527             my $pkg = $urpm->{depslist}[$_] or next;
528             $pkg->flag_disable_obsolete
529             and $inst{$pkg->id} = delete $sources->{$pkg->id};
530             }
531              
532             \%inst;
533             }
534              
535             #- deprecated, use find_candidate_packages_() directly
536             #-
537             #- side-effects: none
538             sub find_candidate_packages_ {
539             my ($urpm, $id_prop) = @_;
540              
541             my %packages;
542             foreach ($urpm->find_candidate_packages($id_prop)) {
543             push @{$packages{$_->name}}, $_;
544             }
545             values %packages;
546             }
547              
548             =item get_updates_description($urpm, @update_medias)
549              
550             Get reason of update for packages to be updated.
551             Use all update medias if none given.
552              
553             =cut
554              
555             sub get_updates_description {
556             my ($urpm, @update_medias) = @_;
557             my %update_descr;
558             my ($cur, $section);
559              
560             @update_medias or @update_medias = urpm::media::non_ignored_media($urpm, 'update');
561              
562             foreach my $medium (@update_medias) {
563             # fix not taking into account the last %package token of each descrptions file: '%package dummy'
564             foreach (cat_utf8(urpm::media::statedir_descriptions($urpm, $medium)),
565             ($::env ? cat_utf8("$::env/descriptions") : ()), '%package dummy') {
566             /^%package +(.+)/ and do {
567             # fixes not parsing descriptions file when MU adds itself the security source:
568             if (exists $cur->{importance} && !member($cur->{importance}, qw(security bugfix))) {
569             $cur->{importance} = 'normal';
570             }
571             $update_descr{$medium->{name}}{$_} = $cur foreach @{$cur->{pkgs} || []};
572             $cur = { pkgs => [ split /\s/, $1 ] };
573             $section = 'pkg';
574             next;
575             };
576             /^Updated?: +(.+)/ && $section eq 'pkg' and do { $cur->{updated} = $1; next };
577             /^Importance: +(.+)/ && $section eq 'pkg' and do { $cur->{importance} = $1; next };
578             /^(ID|URL): +(.+)/ && $section eq 'pkg' and do { $cur->{$1} = $2; next };
579             /^%(pre|description)/ and do { $section = $1; next };
580             $section =~ /^(pre|description)\z/ and $cur->{$1} .= $_;
581             }
582             }
583             \%update_descr;
584             }
585              
586             sub error_restricted ($) {
587             my ($urpm) = @_;
588             $urpm->{fatal}(2, N("This operation is forbidden while running in restricted mode"));
589             }
590              
591             sub DESTROY {}
592              
593             1;
594              
595              
596             =back
597              
598             =head1 SEE ALSO
599              
600             The L package is used to manipulate at a lower level synthesis and rpm
601             files.
602              
603             See also submodules: L, L, L,
604             L, L, L, L,
605             L, L, L, L,
606             L, L, L, L,
607             L, L, L,
608             L, L, L,
609             L, L, L, L,
610             L, L
611              
612             =head1 COPYRIGHT
613              
614             Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 MandrakeSoft SA
615              
616             Copyright (C) 2005-2010 Mandriva SA
617              
618             Copyright (C) 2011-2017 Mageia
619              
620             This program is free software; you can redistribute it and/or modify
621             it under the terms of the GNU General Public License as published by
622             the Free Software Foundation; either version 2, or (at your option)
623             any later version.
624              
625             This program is distributed in the hope that it will be useful,
626             but WITHOUT ANY WARRANTY; without even the implied warranty of
627             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
628             GNU General Public License for more details.
629              
630             You should have received a copy of the GNU General Public License
631             along with this program; if not, write to the Free Software
632             Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
633              
634             =cut
635              
636             # ex: set ts=8 sts=4 sw=4 noet: