File Coverage

blib/lib/urpm/sys.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::sys;
2              
3              
4 1     1   942 use strict;
  1         2  
  1         27  
5 1     1   4 use warnings;
  1         1  
  1         29  
6 1     1   3 use urpm::util 'cat_';
  1         1  
  1         37  
7 1     1   32 use urpm::msg;
  0            
  0            
8             use POSIX ();
9              
10              
11             =head1 NAME
12              
13             urpm::sys - OS-related routines for urpmi
14              
15             =head1 SYNOPSIS
16              
17             =head1 DESCRIPTION
18              
19             =over
20              
21             =cut
22              
23             =item get_packages_list($file, $o_extra)
24              
25             Get the list of packages that should not be upgraded or installed,
26             typically from the inst.list or skip.list files.
27              
28             =cut
29              
30             sub get_packages_list {
31             my ($file, $o_extra) = @_;
32             my @l = split(/,/, $o_extra || '');
33             push @l, cat_($file);
34             [ grep { $_ } map {
35             chomp; s/#.*$//; s/^\s*//; s/\s*$//;
36             $_;
37             } @l ];
38             }
39              
40             sub _read_fstab_or_mtab {
41             my ($file) = @_;
42              
43             my @l;
44             foreach (cat_($file)) {
45             next if /^\s*#/;
46             my ($device, $mntpoint, $fstype, $_options) = m!^\s*(\S+)\s+(/\S+)\s+(\S+)\s+(\S+)!
47             or next;
48             $mntpoint =~ s,/+,/,g; $mntpoint =~ s,/$,,;
49             push @l, { mntpoint => $mntpoint, device => $device, fs => $fstype };
50             }
51             @l;
52             }
53              
54             =item find_a_mntpoint($dir)
55              
56             Find used mount point from a pathname
57              
58             =cut
59              
60             sub find_a_mntpoint {
61             my ($dir) = @_;
62             _find_a_mntpoint($dir, {});
63             }
64              
65             sub read_mtab() { _read_fstab_or_mtab('/etc/mtab') }
66              
67             #- find used mount point from a pathname
68             sub _find_a_mntpoint {
69             my ($dir, $infos) = @_;
70              
71             #- read /etc/fstab and check for existing mount point.
72             foreach (_read_fstab_or_mtab("/etc/fstab")) {
73             $infos->{$_->{mntpoint}} = { mounted => 0, %$_ };
74             }
75             foreach (read_mtab()) {
76             $infos->{$_->{mntpoint}} = { mounted => 1, %$_ };
77             }
78              
79             #- try to follow symlink, too complex symlink graph may not be seen.
80             #- check the possible mount point.
81             my @paths = split '/', $dir;
82             my $pdir = '';
83             while (@paths) {
84             my $path = shift @paths;
85             length($path) or next;
86             $pdir .= "/$path";
87             $pdir =~ s,/+,/,g; $pdir =~ s,/$,,;
88             if (exists($infos->{$pdir})) {
89             #- following symlinks may be useless or dangerous for supermounted devices.
90             #- this means it is assumed no symlink inside a removable device
91             #- will go outside the device itself (or at least will go into
92             #- regular already mounted device like /).
93             #- for simplification we refuse also any other device and stop here.
94             return $infos->{$pdir};
95             } elsif (-l $pdir) {
96             unshift @paths, split '/', _expand_symlink($pdir);
97             $pdir = '';
98             }
99             }
100             undef;
101             }
102              
103             =item df($mntpoint)
104              
105             Return the size of the partition and its free space in KiB
106              
107             =cut
108              
109             sub df {
110             my ($mntpoint) = @_;
111             require Filesys::Df;
112             my $df = Filesys::Df::df($mntpoint || "/", 1024); # ask 1kb values
113             @$df{qw(blocks bfree)};
114             }
115              
116             sub _expand_symlink {
117             my ($pdir) = @_;
118              
119             while (my $v = readlink $pdir) {
120             if ($pdir =~ m|^/|) {
121             $pdir = $v;
122             } else {
123             while ($v =~ s!^\.\./!!) {
124             $pdir =~ s!/[^/]+/*$!!;
125             }
126             $pdir .= "/$v";
127             }
128             }
129             $pdir;
130             }
131              
132             sub whereis_binary {
133             my ($prog, $o_prefix) = @_;
134             if ($prog =~ m!/!) {
135             warn qq(don't call whereis_binary with a name containing a "/" (the culprit is: $prog)\n);
136             return;
137             }
138             my $prefix = $o_prefix || '';
139             foreach (split(':', $ENV{PATH})) {
140             my $f = "$_/$prog";
141             -x "$prefix$f" and return $f;
142             }
143             }
144              
145             sub may_clean_rpmdb_shared_regions {
146             my ($urpm, $test) = @_;
147              
148             if ($urpm->{root} && !$test || $urpm->{tune_rpm}{private}) {
149             $urpm->{root} && $urpm->{debug} and $urpm->{debug}("workaround bug in rpmlib by removing $urpm->{root}/var/lib/rpm/__db*");
150             clean_rpmdb_shared_regions($urpm->{root});
151             }
152             }
153              
154             sub clean_rpmdb_shared_regions {
155             my ($prefix) = @_;
156             unlink glob("$prefix/var/lib/rpm/__db.*");
157             }
158              
159             sub proc_mounts() {
160             my @l = cat_('/proc/mounts') or warn "Can't read /proc/mounts: $!\n";
161             @l;
162             }
163              
164             sub proc_self_mountinfo() {
165             my @l = cat_('/proc/self/mountinfo') or warn "Can't read /proc/self/mountinfo: $!\n";
166             @l;
167             }
168              
169             sub trim_until_d {
170             my ($dir) = @_;
171             foreach (proc_mounts()) {
172             #- fail if an iso is already mounted
173             m!^/dev/loop! and return $dir;
174             }
175             while ($dir && !-d $dir) { $dir =~ s,/[^/]*$,, }
176             $dir;
177             }
178              
179             =item check_fs_writable()
180              
181             Checks if the main filesystems are writable for urpmi to install files in
182              
183             =cut
184              
185             sub check_fs_writable () {
186             foreach (proc_self_mountinfo()) {
187             (undef, undef, undef, undef, our $mountpoint, my $opts) = split ' ';
188             if ($opts =~ /(?:^|,)ro(?:,|$)/ && $mountpoint =~ m!^(/|/usr|/s?bin)\z!) {
189             return 0;
190             }
191             }
192             1;
193             }
194              
195             sub _launched_time {
196             my ($component) = @_;
197              
198             if ($component eq N_("system")) {
199             my ($uptime) = cat_('/proc/uptime') =~ /(\S+)/;
200             time() - $uptime;
201             } else {
202             1; # TODO
203             }
204             }
205              
206             sub need_restart {
207             my ($root) = @_;
208             my $rpm_qf = '%{name} %{installtime} [%{provides}:%{Provideversion} ]\n';
209             my $options = ($root ? "--root $root " : '') . "-q --whatprovides should-restart --qf '$rpm_qf'";
210             open(my $F, "rpm $options | uniq |");
211              
212             my (%need_restart, %launched_time);
213             while (my $line = <$F>) {
214             my ($name, $installtime, $s) = $line =~ /(\S+)\s+(\S+)\s+(.*)/;
215            
216             my @should_restart = $s =~ /should-restart:(\S+)/g;
217             foreach my $component (@should_restart) {
218             $launched_time{$component} ||= _launched_time($component);
219              
220             if ($launched_time{$component} < $installtime) {
221             push @{$need_restart{$component}}, $name;
222             }
223             }
224             }
225             %need_restart && \%need_restart;
226             }
227              
228             sub need_restart_formatted {
229             my ($root) = @_;
230             my $need_restart = need_restart($root) or return;
231              
232             foreach (keys %$need_restart) {
233             my $packages = join(', ', sort @{$need_restart->{$_}});
234             if ($_ eq 'system') {
235             $need_restart->{$_} = N("You should restart your computer for %s", $packages);
236             } elsif ($_ eq 'session') {
237             $need_restart->{$_} = N("You should restart your session for %s", $packages);
238             } else {
239             $need_restart->{$_} = N("You should restart %s for %s", translate($_), $packages);
240             }
241             }
242             $need_restart;
243             }
244              
245             # useful on command-line: perl -Murpm::sys -e 'urpm::sys::print_need_restart'
246             sub print_need_restart() {
247             my $h = need_restart_formatted('');
248             print "$_\n" foreach values %$h;
249             }
250              
251             sub migrate_back_rpmdb_db_to_hash_8 {
252             my ($urpm, $root) = @_;
253              
254             $urpm->{info}("migrating back the created rpm db from Hash version 9 to Hash version 8");
255              
256             foreach my $db_file (glob("$root/var/lib/rpm/[A-Z]*")) {
257             rename $db_file, "$db_file.";
258             system("db_dump $db_file. | db42_load $db_file");
259             if (-e $db_file) {
260             unlink "$db_file.";
261             } else {
262             rename "$db_file.", $db_file;
263             $urpm->{error}("rpm db migration failed on $db_file. You will not be able to run rpm chrooted");
264             return;
265             }
266             }
267             }
268              
269             sub migrate_back_rpmdb_db_to_4_6 {
270             my ($urpm, $root) = @_;
271             $urpm->{info}("migrating back the created rpm db from rpm-4.9 to rpm-4.6/4.8");
272             if (system('chroot', $root, 'rpm', '--rebuilddb') == 0) {
273             $urpm->{log}("rpm db downgraded successfully");
274             } else {
275             $urpm->{error}("rpm db downgrade failed. You will not be able to run rpm chrooted");
276             }
277             }
278              
279             sub migrate_back_rpmdb_db_version {
280             my ($urpm, $root) = @_;
281              
282             if ($urpm->{need_migrate_rpmdb} eq '4.6') {
283             migrate_back_rpmdb_db_to_hash_8($urpm, $root);
284             } elsif ($urpm->{need_migrate_rpmdb} eq '4.8') {
285             migrate_back_rpmdb_db_to_4_6($urpm, $root);
286             }
287              
288             clean_rpmdb_shared_regions($root);
289             }
290              
291              
292             =item apply_delta_rpm($deltarpm, $o_dir, $o_pkg)
293              
294             Create a plain rpm from an installed rpm and a delta rpm (in the current directory)
295             Returns the new rpm filename in case of success.
296             Params :
297              
298             =over
299              
300             =item * $deltarpm : full pathname of the deltarpm
301              
302             =item * $o_dir : directory where to put the produced rpm (optional)
303              
304             =item * $o_pkg : URPM::Package object corresponding to the deltarpm (optional)
305              
306             =back
307              
308             =cut
309              
310             our $APPLYDELTARPM = '/usr/bin/applydeltarpm';
311             sub apply_delta_rpm {
312             my ($deltarpm, $o_dir, $o_pkg) = @_;
313             -x $APPLYDELTARPM or return 0;
314             -e $deltarpm or return 0;
315             my $rpm;
316             if ($o_pkg) {
317             require URPM; #- help perl_checker
318             $rpm = $o_pkg->fullname . '.rpm';
319             } else {
320             $rpm = `rpm -qp --qf '%{name}-%{version}-%{release}.%{arch}.rpm' '$deltarpm'`;
321             }
322             $rpm or return 0;
323             $rpm = $o_dir . '/' . $rpm;
324             unlink $rpm;
325             system($APPLYDELTARPM, $deltarpm, $rpm);
326             -e $rpm ? $rpm : '';
327             }
328              
329             our $tempdir_template = '/tmp/urpm.XXXXXX';
330             sub mktempdir() {
331             my $tmpdir;
332             eval { require File::Temp };
333             if ($@) {
334             #- fall back to external command (File::Temp not in perl-base)
335             $tmpdir = `mktemp -d $tempdir_template`;
336             chomp $tmpdir;
337             } else {
338             $tmpdir = File::Temp::tempdir($tempdir_template);
339             }
340             return $tmpdir;
341             }
342              
343             # temporary hack used by urpmi when restarting itself.
344             sub fix_fd_leak() {
345             opendir my $dirh, "/proc/$$/fd" or return undef;
346             my @fds = grep { /^(\d+)$/ && $1 > 2 } readdir $dirh;
347             closedir $dirh;
348             foreach (@fds) {
349             my $link = readlink("/proc/$$/fd/$_");
350             $link or next;
351             next if $link =~ m!^/(usr|dev)/! || $link !~ m!^/!;
352             POSIX::close($_);
353             }
354             }
355              
356             sub clean_dir {
357             my ($dir) = @_;
358              
359             require File::Path;
360             File::Path::rmtree([$dir]);
361             }
362              
363             sub empty_dir {
364             my ($dir) = @_;
365             clean_dir($dir);
366             mkdir $dir, 0755;
367             }
368              
369             sub syserror {
370             my ($urpm, $msg, $info) = @_;
371             $urpm->{error}("$msg [$info] [$!]");
372             }
373              
374             sub open_safe {
375             my ($urpm, $sense, $filename) = @_;
376             open my $f, $sense, $filename
377             or syserror($urpm, $sense eq '>' ? N("Can't write file") : N("Can't open file"), $filename), return undef;
378             return $f;
379             }
380              
381             sub opendir_safe {
382             my ($urpm, $dirname) = @_;
383             opendir my $d, $dirname
384             or syserror($urpm, "Can't open directory", $dirname), return undef;
385             return $d;
386             }
387              
388             sub move_or_die {
389             my ($urpm, $file, $dest) = @_;
390             urpm::util::move($file, $dest) or $urpm->{fatal}(1, N("Can't move file %s to %s", $file, $dest));
391             }
392              
393             1;
394              
395              
396             =back
397              
398             =head1 COPYRIGHT
399              
400             Copyright (C) 2005 MandrakeSoft SA
401              
402             Copyright (C) 2005-2010 Mandriva SA
403              
404             =cut