summaryrefslogtreecommitdiffstats
path: root/urpm/lock.pm
blob: 6150af68aa1544a6af723e89e70b7dcdd865ec06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package urpm::lock;


use strict;
use urpm::msg;
use urpm::util 'cat_';

#- avoid putting a require on Fcntl ':flock' (which is perl and not perl-base).
my ($LOCK_SH, $LOCK_EX, $LOCK_NB, $LOCK_UN) = (1, 2, 4, 8);



=head1 NAME

urpm::lock - urpm/rpm DB locking related routines for urpmi

=head1 SYNOPSIS

=head1 DESCRIPTION

=over

=cut 


################################################################################
#- class functions


=item rpm_db($urpm, $b_exclusive, %options)

Lock urpmi DB.

Lock policy concerning chroot : lock rpm db in chroot

Options: nofatal, wait

=cut

sub rpm_db {
    my ($urpm, $b_exclusive, %options) = @_;
    my $f = ($urpm->{root} ? "$urpm->{root}/" : '') . "/var/lib/rpm/.RPMLOCK";
    urpm::lock->new($urpm, $f, 'rpm', $b_exclusive, %options);
}

=item urpmi_db($urpm, $b_exclusive, %options)

Lock urpmi DB.

Lock policy concerning chroot : lock urpmi db in /

Options: nofatal, wait

=cut

sub urpmi_db {
    my ($urpm, $b_exclusive, %options) = @_;
    urpm::lock->new($urpm, "$urpm->{statedir}/.LOCK", 'urpmi', $b_exclusive, %options);
}


################################################################################
#- methods

# (options: nofatal, wait)
sub new {
    my ($_class, $urpm, $file, $db_name, $b_exclusive, %options) = @_;
    
    my $fh;
    #- we don't care what the mode is. ">" allow creating the file, but can't be done as user
    open($fh, '>', $file) or open($fh, '<', $file) or return;

    my $lock = bless { 
	fh => $fh, db_name => $db_name, 
	fatal => $options{nofatal} ? $urpm->{error} : sub { $urpm->{fatal}(7, $_[0]) }, 
	info => $urpm->{info},
	log => $urpm->{log},
    };
    _lock($lock, $b_exclusive, $options{wait});
    $lock;
}

sub get_lock_pid {
    my ($fh) = @_;
    my ($dev, $ino, undef) = stat($fh);
    my $major = int($dev/256);
    my $minor = $dev % 256;
    my $fileid = sprintf("%02x:%02x:%d",$major,$minor,$ino);
    foreach (urpm::util::cat_('/proc/locks')) { /FLOCK.*WRITE\s*(\d+)\s*$fileid\s/ && return $1 }
}

sub warn_about_locker {
    my ($lock) = @_;
    if (my $pid = get_lock_pid($lock->{fh})) {
        my $name = urpm::util::cat_("/proc/$pid/cmdline");
        $name =~ tr/\0/ /;
        $name =~ s/ *$//;
        $lock->{fatal}(N("%s database is locked, process %d is already using it", $lock->{db_name}, $pid) . ($name ? " ($name)" : ""));
    } else {
        $lock->{fatal}(N("%s database is locked (another program is already using it)", $lock->{db_name}));
    }
}

sub _lock {
    my ($lock, $b_exclusive, $b_wait) = @_;
    $b_exclusive ||= '';
    if ($lock->{log}) {
	my $action = $lock->{exclusive} && !$b_exclusive ? 'releasing exclusive' : $b_exclusive ? 'getting exclusive' : 'getting';
	$lock->{log}("$action lock on $lock->{db_name}");
    }
    my $mode = $b_exclusive ? $LOCK_EX : $LOCK_SH;
    if (!flock($lock->{fh}, $mode | $LOCK_NB)) {
	if ($b_wait) {
	    $lock->{info}(N("%s database is locked. Waiting...", $lock->{db_name}));
	    flock($lock->{fh}, $mode) or $lock->{fatal}(N("aborting"));
	} else {
            warn_about_locker($lock);
	}
    }
    $lock->{locked} = 1;
    $lock->{exclusive} = $b_exclusive;
}

sub unlock {
    my ($lock) = @_;
    $lock->{fh} or warn "lock $lock->{db_name} already released\n", return;

    if ($lock->{locked}) {
	$lock->{log} and $lock->{log}("unlocking $lock->{db_name} database");
	flock $lock->{fh}, $LOCK_UN;
    }
    close $lock->{fh};
    delete $lock->{fh};
}

sub DESTROY { 
    my ($lock) = @_;
    unlock($lock) if $lock->{fh};
}

=back

=head1 COPYRIGHT

Copyright (C) 2005 MandrakeSoft SA

Copyright (C) 2005-2010 Mandriva SA

Copyright (C) 2011-2017 Mageia

=cut