File Coverage

blib/lib/RPC/Simple/Server.pm
Criterion Covered Total %
statement 21 191 10.9
branch 0 72 0.0
condition 0 14 0.0
subroutine 7 22 31.8
pod 11 15 73.3
total 39 314 12.4


line stmt bran cond sub pod time code
1             package RPC::Simple::Server;
2              
3 1     1   1451 use strict;
  1         2  
  1         37  
4 1         107 use vars qw($VERSION @ISA @EXPORT %pidTab %deadChildren %fhTab $verbose
5 1     1   6 @buddies);
  1         18  
6              
7             # %fhTab is a hash of fileno of file descriptors opened for reading the
8             # STDOUT of children. If contains the ref of the process objects controlling
9             # this child.
10              
11 1     1   5 use Fcntl ;
  1         2  
  1         7111  
12              
13 1     1   7 use IO::Socket ;
  1         2  
  1         11  
14 1     1   7316 use IO::Select ;
  1         2638  
  1         64  
15              
16 1     1   649 use RPC::Simple::ObjectHandler ;
  1         2  
  1         4950  
17              
18             require Exporter;
19              
20             @ISA = qw(Exporter);
21             # Items to export into callers namespace by default. Note: do not export
22             # names by default without a very good reason. Use EXPORT_OK instead.
23             # Do not simply export all your public functions/methods/constants.
24             @EXPORT = qw(mainLoop chilDeath goodGuy registerChild unregisterChild);
25              
26             ( $VERSION ) = '$Revision: 1.8 $ ' =~ /\$Revision:\s+([^\s]+)/;
27              
28             # Preloaded methods go here.
29              
30             # Autoload methods go after =cut, and are processed by the autosplit program.
31              
32             @buddies = ('127.0.0.1') ; # I am a good guy
33             our $verbose = 0;
34              
35             sub childDeath
36             {
37             # not an object method
38             # DO NOT call Tk code in signal handler or in called functions
39 0     0 1   my $pid = wait ;
40 0 0         if (defined $pidTab{$pid})
    0          
41             {
42 0           print "child pid $pid died ($?)\n";
43 0           $deadChildren{$pid} = [$pidTab{$pid}, $?] ;
44 0           delete $pidTab{$pid} ;
45             }
46             elsif (exists $pidTab{$pid})
47             {
48 0           print "old news: child died ($pid)\n" ;
49             }
50             else
51             {
52 0           print "Unknown child died ($pid)\n" ;
53             }
54             # may not be needed anymore according to Tom C TBD
55             }
56              
57 0     0 0   sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }
58              
59             sub mainLoop
60             {
61 0   0 0 1   my $port = shift || 7810 ;
62 0   0       $verbose = shift || 0 ;
63              
64 0           my $clientOpen = 0 ;
65            
66             #create listening socket
67 0           my $server = IO::Socket::INET -> new (Listen => 5,
68             LocalAddr => 'localhost',
69             LocalPort => $port,
70             Proto => 'tcp'
71             ) ;
72            
73 0 0         die "Can't create listening socket $!\n" unless defined $server ;
74              
75 0           my $serverNb = $server -> fileno ;
76              
77 0           logmsg "server started on port $port";
78            
79             # my $sclient = register_io_client
80             # ([],'rw', SERVER ,
81             # \&acceptSocket,\&acceptSocket,\&acceptSocket )
82             # || die "socket server not registered\n";
83            
84             # set_maximum_inactive_server_time(6000) ; # need a handler TBD
85            
86             # print "listening to socket registered\n";
87            
88             # register_interval_client([],5,sub{ print ".";}) ;
89             # start_server() ;
90              
91             # create select object
92 0           my $s = IO::Select -> new() ;
93 0           $s -> add ($server) ; # add listening socket
94            
95 0           while (1)
96             {
97 0           my ($toRead,$dummy,$shutThem) = IO::Select ->
98             select ($s ,undef, $s, 2) ;
99              
100 0           foreach my $fh (@$shutThem)
101             {
102             # close fh on errors (usually dead children, or closed client)
103 0 0         if ($serverNb == $fh->fileno)
104             {
105 0           my $nb = $fh->fileno ;
106 0 0         print "closing fno $nb (on error)\n" if $verbose ;
107 0           my ($theObj,$theMeth) = @{$fhTab{$nb}} ;
  0            
108 0           $theObj-> close(1) ;
109 0           delete $fhTab{$nb} ;
110             }
111             }
112              
113 0           foreach my $fh (@$toRead)
114             {
115 0 0         if ($serverNb == $fh->fileno)
116             {
117             # reading server socket
118 0           my $ref = RPC::Simple::Server -> new($server,$s) ;
119 0 0         next unless defined $ref ;
120 0           my $nb = $ref->getFileno ;
121 0           $fhTab{$nb} = [ $ref , 'readClient' ] ;
122             }
123             else
124             {
125 0           my $nb = $fh->fileno ;
126 0 0         print "reading fno $nb\n" if $verbose ;
127 0           my ($theObj,$theMeth) = @{$fhTab{$nb}} ;
  0            
128 0 0         unless ($theObj-> $theMeth(1) )
129             {
130 0 0         print "closing fno $nb (error after reading)\n"
131             if $verbose ;
132 0           my ($theObj,$theMeth) = @{$fhTab{$nb}} ;
  0            
133 0           $theObj-> close() ;
134 0           delete $fhTab{$nb} ;
135             }
136             }
137             }
138              
139              
140 0           &checkDead ;
141             }
142             }
143              
144             sub registerChild
145             {
146 0     0 1   my $object=shift ;
147 0           my $pid = shift ;
148 0           $pidTab{$pid}=$object;
149             }
150              
151             sub unregisterChild
152             {
153 0     0 1   my $pid = shift ;
154 0           print "Child $pid unregistered\n";
155 0           undef $pidTab{$pid};
156 0           delete $deadChildren{$pid} ;
157             }
158              
159              
160             sub close
161             {
162 0     0 1   my $self= shift ;
163              
164 0           print "closing connection\n";
165 0           $self->{selector}->remove($self->{mySocket}) ;
166             #$self->{mySocket}->close ;
167 0           shutdown($self->{mySocket},2) ;
168             }
169              
170             sub readClient
171             {
172 0     0 1   my $self= shift ;
173              
174             # my ($obj,$key,$handle) = @_ ;
175 0 0         print "readClient called\n" if $verbose ;
176              
177 0 0         return 0 if ($self->{mySocket}->eof) ;
178              
179 0           my @codeTab = () ;
180              
181 0           my $code = '' ;
182 0           my $line ;
183 0           my $codeEnd = 1 ;
184              
185 0   0       while ( $line = $self->{mySocket}->getline or not $codeEnd )
186             {
187 0 0         next unless defined $line ;
188            
189 0 0         print "-> ",$line if $verbose ;
190 0           $code .= $line ;
191 0 0         if ($line =~ /#end$/
192             )
193             {
194 0           push @codeTab, $code ;
195 0           $code = '' ;
196 0           $codeEnd = 1 ;
197             }
198 0 0         if ($line =~ /#begin$/
199             )
200             {
201 0           $codeEnd = 0 ;
202             }
203             }
204              
205 0           foreach $code (@codeTab)
206             {
207 0           my ($args,$method,$reqId,$handle,$objectName) ;
208             # untaint $code and place it in the safe
209              
210 0 0         if ($code =~ m/(.+)/s )
211             {
212 0           $code = $1 ;
213 0 0         print "code is laundered\n" if $verbose ;
214             }
215              
216 0           eval($code) ;
217              
218 0 0         if ($@)
219             {
220 0           print "failed eval ($@) of :\n",$code,"end evaled code\n" ;
221             }
222             else
223             {
224 0 0         print "Call $method \n" if $verbose ;
225              
226 0 0         if ($method eq 'new')
    0          
227             {
228             # create new object, call-back always required
229 0           $self->{handleTab}{$handle} = RPC::Simple::ObjectHandler
230             -> new ($self,$objectName, $handle, $args, $reqId) ;
231             }
232             elsif ($method eq 'destroy')
233             {
234 0           $self->{handleTab}{$handle}->destroy ;
235 0           delete $self->{handleTab}{$handle} ;
236             }
237             else
238             {
239 0           $self->{handleTab}{$handle} ->
240             remoteCall($reqId,$method,$args) ;
241             }
242             }
243             }
244 0 0         print "readClient finished\n" if $verbose ;
245 0           return 1 ;
246             }
247              
248 0     0 0   sub dummy { print "Dummy function called\n"; }
249              
250             sub writeSock
251             {
252 0     0 1   my $self=shift;
253              
254 0           my $handle = shift ; # index of RpcClient
255 0           my $method = shift ;
256 0           my $reqId = shift ;
257 0           my $param = shift ; # usually an array ref
258 0           my $objectName = shift ; # optionnal
259            
260 0           my $refs = [$param,$method,$reqId, $handle ] ;
261 0           my $names = ['args','method','reqId','handle',] ;
262            
263 0 0         if (defined $objectName)
264             {
265 0           push @$refs, $objectName ;
266 0           push @$names, 'objectName' ;
267             }
268            
269 0           my $d = Data::Dumper->new ( $refs, $names ) ;
270 0           my $paramStr = "#begin\n".$d->Dumpxs."#end\n" ;
271             #my $str = sprintf("%6d",length($paramStr)) . $paramStr ;
272 0           my $str = $paramStr ;
273 0 0         print "$paramStr\n" if $verbose ;
274 1     1   14 no strict 'refs' ;
  1         3  
  1         895  
275 0           my $val;
276             eval
277 0           {
278 0           $val = $self->{mySocket}->send($str,0) ;
279             };
280 0 0         warn "send failed $!\n" unless defined $val ;
281 0 0         print "$val bytes sent\n" if $verbose ;
282             }
283              
284             sub new
285             {
286 0     0 0   my $type = shift ;
287 0           my $server = shift ;
288 0           my $selector = shift ;
289             # Optional parameters which can be used to tell server not
290             # to accept the new connection but let the calling routine
291             # do that for us. If these parameters are used, you may
292             # need to override the mainLoop subroutine.
293 0   0       my $socket = shift || undef;
294 0   0       my $manual_accept = shift || 0;
295 0           my $self = {} ;
296              
297 0           $self->{'server'} = $server ;
298 0           $self->{'selector'} = $selector ;
299              
300 0           bless $self, $type;
301              
302 0 0 0       if ($manual_accept && not defined $socket)
303             {
304 0           print "socket required for manual accept mode\n" ;
305 0           undef $self ;
306 0           return undef ;
307             }
308              
309              
310 0           my $iaddr;
311 0 0         unless ($manual_accept)
312             {
313 0           print "Accepting connection\n" ;
314 0           ($socket, $iaddr) = $server -> accept() ; # blocking call
315             }
316              
317 0 0         unless (defined $socket)
318             {
319 0           print "accept failed $!\n" ;
320 0           undef $self ;
321 0           return undef ;
322             }
323              
324 0           print "Connection accepted\n";
325              
326 0           my $name = gethostbyaddr($socket->peeraddr,AF_INET) ;
327 0           my $ipadr = $socket -> peerhost ;
328 0           my $ok = 0 ;
329 0           foreach (@buddies)
330             {
331 0           print "Comparing $ipadr with $_\n";
332 0 0         if ($ipadr eq $_)
333             {
334 0           $ok = 1 ;
335 0           last;
336             }
337             }
338              
339 0 0         unless ($ok)
340             {
341 0           logmsg "connection from $name refused [ $ipadr ]";
342 0           $socket->close ;
343 0           undef $self ;
344 0           return undef ;
345             }
346              
347 0           $self->{mySocket} = $socket ;
348 0 0         $selector->add($socket) unless($manual_accept) ;
349              
350             # put the socket in non-blocking mode
351 0 0         fcntl($socket,F_SETFL, O_NDELAY) || die "fcntl failed $!\n";
352              
353 0           logmsg "connection from $name [ $ipadr ] ";
354 0           return $self ;
355             }
356              
357              
358             # register an object/method to call
359             sub setMask
360             {
361 0     0 1   my $obj = shift ;
362 0           my $method = shift ;
363 0           my $nb = shift ;
364 0           $fhTab{$nb} = [ $obj , $method ] ;
365             }
366              
367             sub resetMask
368             {
369 0     0 1   my $nb = shift ;
370 0           delete $fhTab{$nb} ;
371             }
372              
373             sub checkDead
374             {
375 0 0   0 0   if (scalar %deadChildren )
376             {
377 0           my $pid ;
378 0           foreach $pid (keys %deadChildren)
379             {
380 0           my ($ref,$out) = @{$deadChildren{$pid}};
  0            
381 0           $ref->processOver($out) ;
382 0           delete $deadChildren{$pid} ;
383             }
384             }
385             }
386              
387             sub getFileno
388             {
389 0     0 1   my $self = shift ;
390 0           return $self->{mySocket}->fileno ;
391             }
392              
393             sub goodGuy
394             {
395 0     0 1   my $good = shift ;
396              
397 0 0         if ($good =~ /^[\d\.]+$/)
398             {
399 0           push @buddies , $good ;
400             }
401             else
402             {
403 0           my (@addrs) = (gethostbyname($good))[4] ;
404 0           my $addr = join(".", unpack('C4', $addrs[0])) ;
405 0           push @buddies, $addr ;
406             }
407             }
408              
409             1;
410             __END__