File Coverage

blib/lib/X10/Server.pm
Criterion Covered Total %
statement 21 97 21.6
branch 0 24 0.0
condition 0 10 0.0
subroutine 7 16 43.7
pod 0 6 0.0
total 28 153 18.3


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