File Coverage

blib/lib/DDLock/Client/Daemon.pm
Criterion Covered Total %
statement 47 94 50.0
branch 5 22 22.7
condition 2 9 22.2
subroutine 11 16 68.7
pod 0 7 0.0
total 65 148 43.9


line stmt bran cond sub pod time code
1             package DDLock::Client::Daemon;
2 3     3   16 use strict;
  3         5  
  3         127  
3 3     3   15 use Socket qw{:DEFAULT :crlf};
  3         5  
  3         3802  
4 3     3   3969 use IO::Socket::INET ();
  3         81367  
  3         99  
5              
6 3     3   35 use constant DEFAULT_PORT => 7002;
  3         5  
  3         268  
7 3     3   19 use constant DEBUG => 0;
  3         6  
  3         153  
8              
9 3     3   2602 use fields qw( name sockets pid client hooks );
  3         4838  
  3         47  
10              
11              
12             ### (CONSTRUCTOR) METHOD: new( $client, $name, @socket_names )
13             ### Create a new lock object that corresponds to the specified I and is
14             ### held by the given I.
15             sub new {
16 3     3 0 7 my DDLock $self = shift;
17 3 50       13 $self = fields::new( $self ) unless ref $self;
18              
19 3         235 $self->{client} = shift;
20 3         7 $self->{name} = shift;
21 3         10 $self->{pid} = $$;
22 3         10 $self->{sockets} = $self->getlocks(@_);
23 0         0 $self->{hooks} = {}; # hookname -> coderef
24 0         0 return $self;
25             }
26              
27              
28             ### (PROTECTED) METHOD: getlocks( @servers )
29             ### Try to obtain locks with the specified I from one or more of the
30             ### given I.
31             sub getlocks {
32 3     3 0 4 my DDLock $self = shift;
33 3         6 my $lockname = $self->{name};
34 3         6 my @servers = @_;
35              
36 3         5 my @addrs = ();
37              
38             my $fail = sub {
39 0     0   0 my $msg = shift;
40             # release any locks that we did get:
41 0         0 foreach my $addr (@addrs) {
42 0 0       0 my $sock = $self->{client}->get_sock($addr)
43             or next;
44 0         0 $sock->printf("releaselock lock=%s%s", eurl($self->{name}), CRLF);
45 0         0 my $result = <$sock>;
46 0         0 warn $result if DEBUG;
47             }
48 0         0 die $msg;
49 3         14 };
50              
51             # First create connected sockets to all the lock hosts
52 3         7 SERVER: foreach my $server ( @servers ) {
53 3         9 my ( $host, $port ) = split /:/, $server;
54 3   50     16 $port ||= DEFAULT_PORT;
55 3         7 my $addr = "$host:$port";
56              
57 3 50       12 my $sock = $self->{client}->get_sock($addr)
58             or next SERVER;
59              
60 0         0 $sock->printf( "trylock lock=%s%s", eurl($lockname), CRLF );
61 0         0 chomp( my $res = <$sock> );
62 0 0       0 $fail->("$server: '$lockname' $res\n") unless $res =~ m{^ok\b}i;
63              
64 0         0 push @addrs, $addr;
65             }
66              
67 3 50       2404 die "No available lock hosts" unless @addrs;
68 0         0 return \@addrs;
69             }
70              
71             sub name {
72 0     0 0 0 my DDLock $self = shift;
73 0         0 return $self->{name};
74             }
75              
76             sub set_hook {
77 0     0 0 0 my DDLock $self = shift;
78 0   0     0 my $hookname = shift || return;
79              
80 0 0       0 if (@_) {
81 0         0 $self->{hooks}->{$hookname} = shift;
82             } else {
83 0         0 delete $self->{hooks}->{$hookname};
84             }
85             }
86              
87             sub run_hook {
88 3     3 0 5 my DDLock $self = shift;
89 3   50     10 my $hookname = shift || return;
90              
91 3 50       14 if (my $hook = $self->{hooks}->{$hookname}) {
92 0         0 local $@;
93 0         0 eval { $hook->($self) };
  0         0  
94 0 0       0 warn "DDLock hook '$hookname' threw error: $@" if $@;
95             }
96             }
97              
98             sub DESTROY {
99 3     3   7 my DDLock $self = shift;
100              
101 3         9 $self->run_hook('DESTROY');
102 3         5 local $@;
103 3         5 eval { $self->_release_lock(@_) };
  3         10  
104              
105 3         28 return;
106             }
107              
108             ### METHOD: release()
109             ### Release the lock held by the lock object. Returns the number of sockets that
110             ### were released on success, and dies with an error on failure.
111             sub release {
112 0     0 0 0 my DDLock $self = shift;
113              
114 0         0 $self->run_hook('release');
115 0         0 return $self->_release_lock(@_);
116             }
117              
118             sub _release_lock {
119 3     3   4 my DDLock $self = shift;
120              
121 3         6 my $count = 0;
122              
123 3 50       13 my $sockets = $self->{sockets} or return;
124              
125             # lock server might have gone away, but we don't really care.
126 0           local $SIG{'PIPE'} = "IGNORE";
127              
128 0           foreach my $addr (@$sockets) {
129 0 0         my $sock = $self->{client}->get_sock_onlycache($addr)
130             or next;
131              
132 0           my $res;
133              
134 0           eval {
135 0           $sock->printf("releaselock lock=%s%s", eurl($self->{name}), CRLF);
136 0           $res = <$sock>;
137 0           chomp $res;
138             };
139              
140 0 0 0       if ($res && $res !~ m/ok\b/i) {
141 0           my $port = $sock->peerport;
142 0           my $addr = $sock->peerhost;
143 0           die "releaselock ($addr): $res\n";
144             }
145              
146 0           $count++;
147             }
148              
149 0           return $count;
150             }
151              
152              
153             ### FUNCTION: eurl( $arg )
154             ### URL-encode the given I and return it.
155             sub eurl
156             {
157 0     0 0   my $a = $_[0];
158 0           $a =~ s/([^a-zA-Z0-9_,.\\: -])/uc sprintf("%%%02x",ord($1))/eg;
  0            
159 0           $a =~ tr/ /+/;
160 0           return $a;
161             }
162              
163             1;
164              
165              
166             # Local Variables:
167             # mode: perl
168             # c-basic-indent: 4
169             # indent-tabs-mode: nil
170             # End: