File Coverage

blib/lib/File/FDkeeper/Server.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package File::FDkeeper::Server ;
2             @ISA = qw(File::FDkeeper) ;
3              
4 3     3   17 use strict ;
  3         5  
  3         179  
5 3     3   2318 use File::FDpasser ;
  0            
  0            
6             use File::FDkeeper ;
7             use Digest::MD5 qw(md5_hex) ;
8             use IO::Select ;
9             use Carp ;
10              
11              
12             sub new {
13             my $class = shift ;
14             my $path = shift ;
15             my %args = @_ ;
16              
17             my $this = {} ;
18             $this->{path} = $path ;
19             $this->{timeout} = delete $args{AccessTimeout} || undef ;
20             $this->{timeout_check} = delete $args{AccessTimeoutCheck} || undef ;
21             bless($this, $class) ;
22              
23             while (my ($k, $v) = each %args){
24             croak("Invalid attribute '$k'") ;
25             }
26              
27             if (-e $path){
28             croak("Can't unlink '$path': $!") unless unlink($path) ;
29             }
30             my $server = endp_create($path) ;
31             croak("Error creating server endpoint '$path': $!") unless $server ;
32              
33             $this->{server} = $server ;
34             $this->{next_fhid} = 1 ;
35             $this->{locker} = {} ;
36              
37             return $this ;
38             }
39              
40              
41             sub DESTROY {
42             my $this = shift ;
43              
44             close($this->{server}) unless ! defined($this->{server}) ;
45             }
46              
47              
48             sub run {
49             my $this = shift ;
50             my $llfh = shift ;
51              
52             my $select = new IO::Select($this->{server}) ;
53             # Add the lifeline filehandle
54             $select->add($llfh) if $llfh ;
55              
56             while (1){
57             my @ready = $select->can_read($this->{timeout_check}) ;
58             foreach my $fh (@ready){
59             if (($llfh)&&($fh eq $llfh)){
60             # The lifeline is broken, so we die also.
61             CORE::exit(0) ;
62             }
63             elsif ($fh eq $this->{server}){
64             my $client = serv_accept_fh($fh) ;
65             next if ! defined($client) ;
66             $client->autoflush(1) ;
67             $select->add($client) ;
68             }
69             else {
70             my @resp = () ;
71             eval {
72             my $cmd = $this->_read_command($fh) ;
73             if (! defined($cmd)){
74             $select->remove($fh) ;
75             no warnings ;
76             next ;
77             }
78              
79             if ($cmd eq 'put'){
80             my $recvd_fh = recv_fh($fh) or die("Error receiving filehandle: $!") ;
81             my $fhid = $this->put($recvd_fh) ;
82             @resp = (1, $fhid, undef) ;
83             }
84             elsif ($cmd eq 'get'){
85             my $fhid = <$fh> ;
86             chomp($fhid) ;
87             my $sent_fh = $this->get($fhid) ;
88             @resp = ($sent_fh ?
89             (1, '', $sent_fh) :
90             (0, "Unknown filehandle '$fhid'", undef)) ;
91             }
92             elsif($cmd eq 'del'){
93             my $fhid = <$fh> ;
94             chomp($fhid) ;
95             @resp = ($this->del($fhid) ?
96             (1, '', undef) :
97             (0, "Unknown filehandle '$fhid'", undef)) ;
98             }
99             elsif($cmd eq 'cnt'){
100             @resp = (1, $this->cnt(), undef) ;
101             }
102             else {
103             @resp = (0, "Invalid command '$cmd'", undef) ;
104             }
105              
106             my ($resp_code, $resp_data, $resp_fh) = @resp ;
107             if (! $resp_code){
108             $resp_code = 'err' ;
109             $resp_data =~ s/\r?\n/'\n'/g ;
110             $resp_data .= "\n" ;
111             }
112             else {
113             if ($resp_fh){
114             $resp_code = 'okh' ;
115             $resp_data = '' ;
116             }
117             elsif (defined($resp_data)){
118             $resp_code = 'okl' ;
119             $resp_data .= "\n" ;
120             }
121             else {
122             $resp_code = 'okn' ;
123             }
124             }
125            
126             print $fh "$resp_code$resp_data" or die("Error writing response: $!") ;
127             if ($resp_fh){
128             send_file($fh, $resp_fh) or die("Error sending filehandle: $!") ;
129             }
130             } ;
131             if ($@){
132             carp($@) ;
133             $select->remove($fh) ;
134             close($fh) ;
135             }
136             }
137             }
138              
139             # Delete expired filehandles
140             if ((defined($this->{timeout}))&&($this->{timeout} > 0)){
141             my $now = time() ;
142             foreach my $id (keys %{$this->{locker}}){
143             my $atime = $this->{locker}->{$id}->{atime} ;
144             if (($now - $atime) > $this->{timeout}){
145             $this->del($id) ;
146             }
147             }
148             }
149             }
150             }
151              
152              
153             sub get_fh_id {
154             my $this = shift ;
155             my $fh = shift ;
156              
157             my $fhid = undef ;
158             do { $fhid = md5_hex(time() . "$fh" . $this->{next_fhid}) }
159             while (exists $this->{locker}->{$fhid}) ;
160              
161             return $fhid ;
162             }
163              
164              
165             sub put {
166             my $this = shift ;
167             my $fh = shift ;
168              
169             my $fhid = $this->get_fh_id($fh) ;
170             $this->{locker}->{$fhid} = {
171             fh => $fh,
172             atime => time(),
173             } ;
174            
175             return $fhid ;
176             }
177              
178              
179             sub get {
180             my $this = shift ;
181             my $fhid = shift ;
182              
183             my $entry = $this->{locker}->{$fhid} ;
184             return undef unless $entry ;
185              
186             $entry->{atime} = time() ;
187             return $entry->{fh} ;
188             }
189              
190              
191             sub del {
192             my $this = shift ;
193             my $fhid = shift ;
194              
195             my $entry = delete $this->{locker}->{$fhid} ;
196             return 0 unless $entry ;
197              
198             # shutdown also closes the same handle in other processes.
199             shutdown($entry->{fh}, 2) ;
200              
201             return 1 ;
202             }
203              
204              
205             sub cnt {
206             my $this = shift ;
207              
208             return scalar(keys %{$this->{locker}}) ;
209             }
210              
211              
212              
213             1 ;