File Coverage

blib/lib/X10/Server.pm
Criterion Covered Total %
statement 21 98 21.4
branch 0 24 0.0
condition 0 10 0.0
subroutine 7 16 43.7
pod 0 6 0.0
total 28 154 18.1


line stmt bran cond sub pod time code
1             package X10::Server;
2              
3             # this is a network server object that accepts connections via a TCP
4             # socket and relays the 'event requests' to an
5             # X10::Controller-type object
6              
7 1     1   5 use File::Basename;
  1         2  
  1         60  
8 1     1   5 use FileHandle;
  1         1  
  1         10  
9 1     1   1124 use IO::Socket;
  1         16132  
  1         5  
10 1     1   540 use Storable qw(thaw);
  1         3  
  1         64  
11              
12 1     1   4 use strict;
  1         2  
  1         29  
13              
14 1     1   5 use X10::Event;
  1         2  
  1         27  
15 1     1   493 use X10::EventList;
  1         3  
  1         824  
16              
17             sub new
18             {
19 0     0 0   my $type = shift;
20              
21 0           my $self = bless { @_ }, $type;
22              
23 0 0         return undef unless ( $self->{controller} );
24              
25 0   0       $self->{server_port} ||= 2020;
26 0   0 0     $self->{logger} ||= sub { $self->syslog(@_) };
  0            
27              
28 0 0         $self->{logger}->('info', "Using TCP port %s", $self->{server_port})
29             if $self->{debug};
30              
31 0           $self->{listen_socket} = new IO::Socket(
32             Domain => &AF_INET,
33             Proto => 'tcp',
34             LocalPort => $self->{server_port},
35             Listen => 5,
36             Reuse => 1,
37             MultiHomed => 1,
38             );
39              
40 0 0         unless ($self->{listen_socket})
41             {
42 0           warn "Problem listening on socket: ", $!;
43 0           return undef;
44             }
45              
46 0           $self->{connected_sockets} = [];
47              
48 0           $self->{controller}->register_listener($self->event_callback);
49              
50 0     0     $SIG{PIPE} = sub {}; # Ignore SIGPIPE
  0            
51              
52 0           return $self;
53             }
54              
55             sub select_fds
56             {
57 0     0 0   my $self = shift;
58              
59 0           @{$self->{connected_sockets}} =
  0            
60 0           grep {$_}
61 0           @{$self->{connected_sockets}};
62              
63             return
64 0           map { $_->fileno }
  0            
65 0           ($self->{listen_socket}, @{$self->{connected_sockets}});
66             }
67              
68             sub handle_input
69             {
70 0     0 0   my $self = shift;
71              
72 0           my $allfd = '';
73 0           foreach ($self->select_fds) { vec($allfd, $_, 1) = 1; }
  0            
74              
75 0           my $reads;
76             my $errors;
77              
78 0           my $fdcount = select($reads=$allfd, undef, $errors=$allfd, 0);
79              
80 0 0         return unless ($fdcount);
81              
82 0           FILEHANDLE:
83 0           foreach (@{$self->{connected_sockets}})
84             {
85             # if ( ord($reads) & (1 << $_->fileno) )
86 0 0         if ( vec($reads, $_->fileno, 1) )
87             {
88 0           my $size;
89 0           my $bytes_read = $_->sysread($size, 1);
90              
91 0 0         unless ($bytes_read == 1)
92             {
93 0 0         $self->{logger}->('info',
94             "Disconnecting socket %s", $_->fileno) if $self->{debug};
95 0           undef $_;
96 0           next FILEHANDLE;
97             }
98              
99 0           $size = ord($size);
100              
101 0           my $packet = '';
102 0           $bytes_read = $_->sysread($packet, $size);
103              
104 0 0         unless ($bytes_read == $size)
105             {
106 0           warn "Error reading packet on socket %s", $_->fileno;
107 0           undef $_;
108 0           next FILEHANDLE;
109             }
110              
111 0           my $event = thaw($packet);
112              
113 0 0         next FILEHANDLE unless $event;
114              
115 0 0 0       if ($event->isa('X10::Event') || $event->isa('X10::EventList'))
116             {
117 0   0       $self->{logger}->('info', "From %s: %s",
118             gethostbyaddr($_->peeraddr, AF_INET) || $_->peerhost,
119             $event->as_string
120             );
121 0           $self->{controller}->send($event);
122             }
123             else
124             {
125 0           $self->{logger}->('info', "Unknown packet type: %s", ref $event);
126             }
127             }
128             }
129              
130 0 0         if ( ord($reads) & (1 << $self->{listen_socket}->fileno) )
131             {
132 0           my $newsocket = $self->{listen_socket}->accept;
133 0 0         $self->{logger}->('info', "New connection on %s", $newsocket->fileno) if $self->{debug};
134 0           push @{$self->{connected_sockets}}, $newsocket;
  0            
135             }
136              
137             }
138              
139             sub event_callback
140             {
141 0     0 0   my $self = shift;
142 0     0     return sub { $self->handle_event(shift) };
  0            
143             }
144              
145             sub handle_event
146             {
147 0     0 0   my $self = shift;
148 0           my $event = shift;
149 0           my $packet = $event->nfreeze;
150              
151 0           foreach (@{$self->{connected_sockets}})
  0            
152             {
153 0           $_->syswrite(chr(length($packet)), 1);
154 0           $_->syswrite($packet, length($packet));
155             }
156             }
157              
158              
159             ###
160              
161             sub syslog
162             {
163 0     0 0   my $level = shift;
164 0           my $format = shift;
165 0           my $message = sprintf($format, @_);
166              
167 0           my $facility = "local5";
168 0           my $tag = sprintf "%s[%s]",
169             basename($0, ".pl"),
170             $$,
171             ;
172              
173 0           my $fh = new FileHandle;
174 0           $fh->open("|/usr/bin/logger -p $facility.$level -t $tag");
175              
176 0           $fh->print($message);
177              
178 0           $fh->close;
179             }
180              
181              
182              
183              
184             1;
185