line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package NetServer::Generic; |
4
|
|
|
|
|
|
|
|
5
|
14
|
|
|
14
|
|
110
|
use Carp; |
|
14
|
|
|
|
|
30
|
|
|
14
|
|
|
|
|
1818
|
|
6
|
14
|
|
|
14
|
|
19500
|
use Data::Dumper; |
|
14
|
|
|
|
|
143170
|
|
|
14
|
|
|
|
|
1022
|
|
7
|
14
|
|
|
14
|
|
112
|
use Exporter; |
|
14
|
|
|
|
|
28
|
|
|
14
|
|
|
|
|
402
|
|
8
|
14
|
|
|
14
|
|
70
|
use Fcntl; |
|
14
|
|
|
|
|
22
|
|
|
14
|
|
|
|
|
3758
|
|
9
|
14
|
|
|
14
|
|
10864
|
use IO::File; |
|
14
|
|
|
|
|
142710
|
|
|
14
|
|
|
|
|
3848
|
|
10
|
14
|
|
|
14
|
|
11642
|
use IO::Socket; |
|
14
|
|
|
|
|
244018
|
|
|
14
|
|
|
|
|
64
|
|
11
|
14
|
|
|
14
|
|
8218
|
use IO::Handle; |
|
14
|
|
|
|
|
36
|
|
|
14
|
|
|
|
|
470
|
|
12
|
14
|
|
|
14
|
|
10692
|
use IO::Select; |
|
14
|
|
|
|
|
23048
|
|
|
14
|
|
|
|
|
588
|
|
13
|
14
|
|
|
14
|
|
1202810
|
use IO::Pipe; |
|
14
|
|
|
|
|
22392
|
|
|
14
|
|
|
|
|
664
|
|
14
|
14
|
|
|
14
|
|
14266
|
use POSIX qw(mkfifo BUFSIZ EWOULDBLOCK WNOHANG); |
|
14
|
|
|
|
|
121380
|
|
|
14
|
|
|
|
|
130
|
|
15
|
14
|
|
|
14
|
|
21024
|
use Socket; |
|
14
|
|
|
|
|
34
|
|
|
14
|
|
|
|
|
16722
|
|
16
|
14
|
|
|
14
|
|
28694
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
14
|
|
|
|
|
26070
|
|
|
14
|
|
|
|
|
74
|
|
17
|
14
|
|
|
14
|
|
25628
|
use Tie::RefHash; |
|
14
|
|
|
|
|
29982
|
|
|
14
|
|
|
|
|
828
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
BEGIN { |
20
|
14
|
50
|
|
14
|
|
876
|
if (! eval "require Thread") { |
21
|
|
|
|
|
|
|
# want warnings? uncomment the next line |
22
|
|
|
|
|
|
|
# warn "Could not import Thread.pm: $@\n"; |
23
|
14
|
|
|
|
|
4242
|
$MAIN::no_thread = 1; |
24
|
|
|
|
|
|
|
} else { |
25
|
0
|
|
|
|
|
0
|
Thread->import(); |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
@ISA = (qw(NetServer)); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
$VERSION = "1.03"; |
32
|
|
|
|
|
|
|
|
33
|
14
|
|
|
14
|
|
84
|
use strict; |
|
14
|
|
|
|
|
28
|
|
|
14
|
|
|
|
|
91886
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=pod |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 NAME |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Server - generic TCP/IP server class |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 SYNOPSIS |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $server_cb = sub { |
45
|
|
|
|
|
|
|
my ($s) = shift ; |
46
|
|
|
|
|
|
|
print STDOUT "Echo server: type bye to quit, exit ", |
47
|
|
|
|
|
|
|
"to kill the server.\n\n" ; |
48
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
49
|
|
|
|
|
|
|
return if ($tmp =~ /^bye/i); |
50
|
|
|
|
|
|
|
$s->quit() if ($tmp =~ /^exit/i); |
51
|
|
|
|
|
|
|
print STDOUT "You said:>$tmp\n"; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
my ($foo) = new NetServer::Generic; |
54
|
|
|
|
|
|
|
$foo->port(9000); |
55
|
|
|
|
|
|
|
$foo->callback($server_cb); |
56
|
|
|
|
|
|
|
$foo->mode("forking"); |
57
|
|
|
|
|
|
|
print "Starting server\n"; |
58
|
|
|
|
|
|
|
$foo->run(); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 DESCRIPTION |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
C provides a (very) simple server daemon for TCP/IP |
63
|
|
|
|
|
|
|
processes. It is intended to free the programmer from having to think |
64
|
|
|
|
|
|
|
too hard about networking issues so that they can concentrate on |
65
|
|
|
|
|
|
|
doing something useful. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
The C object accepts the following methods, which |
68
|
|
|
|
|
|
|
configure various aspects of the new server: |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=over 4 |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=item port |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
The port to listen on. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=item hostname |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
The local address to bind to. If no address is specified, listens for |
79
|
|
|
|
|
|
|
any connection on the designated port. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item listen |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Queue size for listen. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item proto |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Protocol we're listening to (defaults to tcp) |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item timeout |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Timeout value (see L) |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item allowed |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
list of IP addresses or hostnames that are explicitly allowed to connect |
96
|
|
|
|
|
|
|
to the server. If empty, the default policy is to allow connections from |
97
|
|
|
|
|
|
|
anyone not in the 'forbidden' list. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
NOTE: IP addresses or hostnames may be specified as perl regular |
100
|
|
|
|
|
|
|
expressions; for example 154\.153\.4\..* matches any IP address |
101
|
|
|
|
|
|
|
beginning with '154.153.4.'; |
102
|
|
|
|
|
|
|
.*antipope\.org matches any hostname in the antipope.org domain. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item forbidden |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
list of IP addresses or hostnames that are refused permission to |
107
|
|
|
|
|
|
|
connect to the server. If empty, the default policy is to refuse |
108
|
|
|
|
|
|
|
connections from anyone not in the 'allowed' list (unless the |
109
|
|
|
|
|
|
|
allowed list is empty, in which case anyone may connect). |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item callback |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Coderef to a subroutine which handles incoming connections (called |
114
|
|
|
|
|
|
|
with one parameter -- a C object which can be used |
115
|
|
|
|
|
|
|
to shut down the session). |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item mode |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Can be one of B, B |
120
|
|
|
|
|
|
|
B, or B. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
By default, B mode is selected. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
B mode is selected, the server handles requests by forking a |
125
|
|
|
|
|
|
|
child process to service them. If B |
126
|
|
|
|
|
|
|
uses the C class to implement a simple non-forking server. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The select-based server may block on i/o on a heavily-loaded system. If |
129
|
|
|
|
|
|
|
you need to do non-blocking i/o you should look at NetServer::FastSelect. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
The B mode is special; it indicates that rather than sitting |
132
|
|
|
|
|
|
|
around waiting for an incoming connection, the server is itself a |
133
|
|
|
|
|
|
|
TCP/IP client. In client mode, C is the B host to |
134
|
|
|
|
|
|
|
connect to and C is the remote port to open. The callback |
135
|
|
|
|
|
|
|
routine is used, as elsewhere, but it should be written as for a |
136
|
|
|
|
|
|
|
client -- i.e. it should issue a request or command, then read. |
137
|
|
|
|
|
|
|
An additional method exists for client mode: C. C |
138
|
|
|
|
|
|
|
expects a coderef as a parameter. This coderef is executed |
139
|
|
|
|
|
|
|
before the client-mode server spawns a child; if it returns a non-zero |
140
|
|
|
|
|
|
|
value the child is forked and opens a client connection to the target |
141
|
|
|
|
|
|
|
host, otherwise the server exits. The trigger method may be used to |
142
|
|
|
|
|
|
|
sleep for a random interval then return 1 (so that repeated clients |
143
|
|
|
|
|
|
|
are spawned at random intervals), or fork several children (on a one- |
144
|
|
|
|
|
|
|
time-only basis) then work as above (so that several clients poke at |
145
|
|
|
|
|
|
|
the target server on a random basis). The default trigger method |
146
|
|
|
|
|
|
|
returns 1 immediately the first time it is called, then returns 0 -- |
147
|
|
|
|
|
|
|
this means that the client makes a single connection to the target |
148
|
|
|
|
|
|
|
host, invokes the callback routine, then exits. (See the test examples |
149
|
|
|
|
|
|
|
which come with this module for examples of how to use client mode.) |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Note that client mode relies on the fork() system call. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
The B mode indicates that multithreading will be used to |
154
|
|
|
|
|
|
|
service requests. This feature requires Perl 5.005 or higher and a |
155
|
|
|
|
|
|
|
native threads library to run, so it's not 100% portable). Moreover, |
156
|
|
|
|
|
|
|
it's unreliable! Don't use this mode unless you're prepared to do some |
157
|
|
|
|
|
|
|
debugging. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The B mode indicates that the server will bind to the |
160
|
|
|
|
|
|
|
designated port, then fork repeatedly up to C<$start_servers> times |
161
|
|
|
|
|
|
|
(where C is a scalar parameter to C). |
162
|
|
|
|
|
|
|
Each child then enters a select-based loop. (i.e. run_select), but exits |
163
|
|
|
|
|
|
|
after handling C<$server_lifespan> transactions (where C |
164
|
|
|
|
|
|
|
is another parameter to C). Every time a child |
165
|
|
|
|
|
|
|
handles a transaction it writes its PID and generation number down a pipe |
166
|
|
|
|
|
|
|
to the parent process, with a message when it exits. The parent keeps |
167
|
|
|
|
|
|
|
track of how many servers are in use and fires up extra children (up to |
168
|
|
|
|
|
|
|
C<$max_servers>) if the number in use leaves less than C<$min_spare_servers> |
169
|
|
|
|
|
|
|
free. See the example B for a minimal HTTP 0.9 server |
170
|
|
|
|
|
|
|
implemented using the B mode. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=back |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Of these, the C method is most important; it specifies |
176
|
|
|
|
|
|
|
a reference to a subroutine which effectively does whatever the |
177
|
|
|
|
|
|
|
server does. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
A callback subroutine is a normal Perl subroutine. It is invoked |
180
|
|
|
|
|
|
|
with STDIN and STDOUT attached to an C object, |
181
|
|
|
|
|
|
|
so that reads from STDIN get information from the client, and writes |
182
|
|
|
|
|
|
|
to STDOUT send information to the client. Note that both STDIN and |
183
|
|
|
|
|
|
|
STDOUT are unbuffered. In addition, a C object is |
184
|
|
|
|
|
|
|
passed as an argument (but the C is free to ignore it). |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Your server reads and writes data via the socket as if it is the |
187
|
|
|
|
|
|
|
standard input and standard output filehandles; for example: |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
while (defined ($tmp = )) { # read a line from the socket |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
print STDOUT "You said: $tmp\n"; # print something to the socket |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
(See C and C for more information on this.) |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
If you're not familiar with sockets, don't get too fresh and try to |
196
|
|
|
|
|
|
|
close or seek on STDIN or STDOUT; just treat them like a file. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
The server object is not strictly necessary in the callback, but comes |
199
|
|
|
|
|
|
|
in handy: you can shut down the server completely by calling the |
200
|
|
|
|
|
|
|
C method. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
When writing a callback subroutine, remember to define some condition under |
203
|
|
|
|
|
|
|
which you return! |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Here's a slightly more complex server example: |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# minimal http server (HTTP/0.9): |
209
|
|
|
|
|
|
|
# this is a REALLY minimal HTTP server. It only understands GET |
210
|
|
|
|
|
|
|
# requests, does no header parsing whatsoever, and doesn't understand |
211
|
|
|
|
|
|
|
# relative addresses! Nor does it understand CGI scripts. And it ain't |
212
|
|
|
|
|
|
|
# suitable as a replacement for Apache (at least, not any time soon :). |
213
|
|
|
|
|
|
|
# The base directory for the server and the default |
214
|
|
|
|
|
|
|
# file name are defined in B, which maps URLs to |
215
|
|
|
|
|
|
|
# absolute pathnames. The server code itself is defined in the |
216
|
|
|
|
|
|
|
# closure B<$http>, which shows how simple it is to write a server |
217
|
|
|
|
|
|
|
# using this module. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub url_to_file($) { |
220
|
|
|
|
|
|
|
# for a given URL, turn it into an absolute pathname |
221
|
|
|
|
|
|
|
my ($u) = shift ; # incoming URL fragment from GET request |
222
|
|
|
|
|
|
|
my ($f) = ""; # file pathname to return |
223
|
|
|
|
|
|
|
my ($htbase) = "/usr/local/etc/httpd/docs/"; |
224
|
|
|
|
|
|
|
my ($htdefault) = "index.html"; |
225
|
|
|
|
|
|
|
chop $u; |
226
|
|
|
|
|
|
|
if ($u eq "/") { |
227
|
|
|
|
|
|
|
$f = $htbase . $htdefault; |
228
|
|
|
|
|
|
|
return $f; |
229
|
|
|
|
|
|
|
} else { |
230
|
|
|
|
|
|
|
if ($u =~ m|^/.+|) { |
231
|
|
|
|
|
|
|
$f = $htbase; chop $f; |
232
|
|
|
|
|
|
|
$f .= $u; |
233
|
|
|
|
|
|
|
} elsif ($u =~ m|[^/]+|) { |
234
|
|
|
|
|
|
|
$f = $htbase . $u; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
if ($u =~ m|.+/$|) { |
237
|
|
|
|
|
|
|
$f .= $htdefault; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
if ($f =~ /\.\./) { |
240
|
|
|
|
|
|
|
my (@path) = split("/", $f); |
241
|
|
|
|
|
|
|
my ($buff, $acc) = ""; |
242
|
|
|
|
|
|
|
shift @path; |
243
|
|
|
|
|
|
|
while ($buff = shift @path) { |
244
|
|
|
|
|
|
|
my ($tmp) = shift @path; |
245
|
|
|
|
|
|
|
if ($tmp ne '..') { |
246
|
|
|
|
|
|
|
unshift @path, $tmp; |
247
|
|
|
|
|
|
|
$acc .= "/$buff"; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
$f = $acc; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
return $f; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
my ($http) = sub { |
257
|
|
|
|
|
|
|
my ($fh) = shift ; |
258
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
259
|
|
|
|
|
|
|
chomp $tmp; |
260
|
|
|
|
|
|
|
if ($tmp =~ /^GET\s+(.*)$/i) { |
261
|
|
|
|
|
|
|
$getfile = $1; |
262
|
|
|
|
|
|
|
$getfile = url_to_file($getfile); |
263
|
|
|
|
|
|
|
print STDERR "Sending $getfile\n"; |
264
|
|
|
|
|
|
|
my ($in) = new IO::File(); |
265
|
|
|
|
|
|
|
if ($in->open("<$getfile") ) { |
266
|
|
|
|
|
|
|
$in->autoflush(1); |
267
|
|
|
|
|
|
|
print STDOUT "Content-type: text/html\n\n"; |
268
|
|
|
|
|
|
|
while (defined ($line = <$in>)) { |
269
|
|
|
|
|
|
|
print STDOUT $line; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} else { |
272
|
|
|
|
|
|
|
print STDOUT "404: File not found\n\n"; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
return 0; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
}; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# main program starts here |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
my (%config) = ("port" => 9000, |
282
|
|
|
|
|
|
|
"callback" => $http, |
283
|
|
|
|
|
|
|
"hostname" => "public.antipope.org"); |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my ($allowed) = ['.*antipope\.org', |
286
|
|
|
|
|
|
|
'.*localhost.*']; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
my ($forbidden) = [ '194\.205\.10\.2']; |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
my ($foo) = new Server(%config); # create new http server bound to port |
291
|
|
|
|
|
|
|
# 9000 of public.antipope.org |
292
|
|
|
|
|
|
|
$foo->allowed($allowed); # who is allowed to connect to us |
293
|
|
|
|
|
|
|
$foo->forbidden($forbidden); # who is refused access |
294
|
|
|
|
|
|
|
print "Starting http server on port 9000\n"; |
295
|
|
|
|
|
|
|
$foo->run(); |
296
|
|
|
|
|
|
|
exit 0; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 Additional methods |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
C provides a couple of extra methods. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=over 4 |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=item peer() |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
The B method returns a reference to a two-element list containing |
308
|
|
|
|
|
|
|
the hostname and IP address of the host at the other end of the socket. |
309
|
|
|
|
|
|
|
If called before a connection has been received, its value will be undefined. |
310
|
|
|
|
|
|
|
(Don't try to assign values via B unless you want to confuse the |
311
|
|
|
|
|
|
|
allowed/forbidden checking code!) |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=item quit() |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
The B method attempts to shut down a server. If running as a forking |
316
|
|
|
|
|
|
|
service, it does so by sending a kill -15 to the parent process. If running |
317
|
|
|
|
|
|
|
as a select-based service it returns from B. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item start_servers() |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
In B mode, specifies how many child servers to start up. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=item max_servers() |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
In B mode, specifies the maximum number of children to spawn |
326
|
|
|
|
|
|
|
under load. |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item min_spare_servers() |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
In B mode, specifies a number of spare (inactive) child |
331
|
|
|
|
|
|
|
servers; if we drop below this level (due to load), the parent will spawn |
332
|
|
|
|
|
|
|
additional children (up to a maximum of B) until we go back |
333
|
|
|
|
|
|
|
over B. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item server_lifespan() |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
In B server mode, child servers run as select servers. After |
338
|
|
|
|
|
|
|
B connections they will commit suicide and be replaced by |
339
|
|
|
|
|
|
|
the parent. If B is set to 1, children will effectively |
340
|
|
|
|
|
|
|
run once then exit (like a forking server). For purposes of insanity, |
341
|
|
|
|
|
|
|
a lifespan of 0 is treated like a lifespan of 1. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item servername() |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
In the B server, unless you I tell the server to bind |
346
|
|
|
|
|
|
|
to a named host, it will accept all incoming connections. Within a client, |
347
|
|
|
|
|
|
|
you may need to know what local IP address an incoming connection was |
348
|
|
|
|
|
|
|
intended for. The C method can be invoked within the child |
349
|
|
|
|
|
|
|
server's callback and returns a two-element arrayref containing the port |
350
|
|
|
|
|
|
|
and IP address that the connection came in on. For example, in the client: |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
my $callback = sub { |
353
|
|
|
|
|
|
|
my $server = shift; |
354
|
|
|
|
|
|
|
my ($server_port, $server_addr) = @{ $server->servername() }; |
355
|
|
|
|
|
|
|
print "Connection on $server_addr:$server_port\n"; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=back |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head2 Types of server |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
A full discussion of internet servers is well beyond the scope of this man |
363
|
|
|
|
|
|
|
page. Beginners may want to start with a source like L
|
364
|
|
|
|
|
|
|
Programming> (which provides a simple, lucid discussion); more advanced |
365
|
|
|
|
|
|
|
readers may find Stevens' L |
366
|
|
|
|
|
|
|
useful. |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
In general, on non-threaded systems, a forking server is slightly less |
369
|
|
|
|
|
|
|
efficient than a select-based server (and uses up lots of PIDs). On the other |
370
|
|
|
|
|
|
|
hand, a select-based server is not a good solution to high workloads or |
371
|
|
|
|
|
|
|
time-consuming processes such as providing an NNTP news feed to an online |
372
|
|
|
|
|
|
|
newsreader. |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
A major issue with the select-based server code in this release is that |
375
|
|
|
|
|
|
|
the IO::Select based server cannot know that a socket is ready until some |
376
|
|
|
|
|
|
|
data is received over it. (It calls B to detect sockets waiting |
377
|
|
|
|
|
|
|
to be read from.) Thus, it is not suitable for writing servers like |
378
|
|
|
|
|
|
|
which emit status information without first reading a request. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 SEE ALSO |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
L, |
384
|
|
|
|
|
|
|
L, |
385
|
|
|
|
|
|
|
L, |
386
|
|
|
|
|
|
|
L, |
387
|
|
|
|
|
|
|
L |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head1 BUGS |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
There are two bugs lurking in NetServer::Generic. Or maybe they're |
392
|
|
|
|
|
|
|
design flaws. I don't have time to fix them right now, but maybe |
393
|
|
|
|
|
|
|
you'd like to contribute an hour or two and get your name in the |
394
|
|
|
|
|
|
|
credits? |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Bug the first: |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
NetServer::Generic attempts to make it easy to write a server by letting |
399
|
|
|
|
|
|
|
the programmer concentrate on reading from STDIN and writing to STDOUT. |
400
|
|
|
|
|
|
|
However, this form of i/o is line oriented. NetServer::Generic relies |
401
|
|
|
|
|
|
|
on the buffering and i/o capabilities provided by Perl and IO::Socket |
402
|
|
|
|
|
|
|
respectively. It doesn't buffer its own input. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
This means that in principle a malicious attacker (or just a badly- |
405
|
|
|
|
|
|
|
written client program) can write a stream of bytes to a |
406
|
|
|
|
|
|
|
NetServer::Generic application and, as long as those bytes don't |
407
|
|
|
|
|
|
|
include a "\n", Perl will keep gobbling it up until it runs out of |
408
|
|
|
|
|
|
|
virtual memory. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
This can be fixed by replacing the globbed IO::Socket::INET that is |
411
|
|
|
|
|
|
|
attached to STDIN with something else -- probably an object that presents |
412
|
|
|
|
|
|
|
itself as an IO::Stringy but that does its own buffering, so that it |
413
|
|
|
|
|
|
|
will return I a line, or some sort of error message in $! if |
414
|
|
|
|
|
|
|
it sees something undigestible in its input stream. (If anyone wants |
415
|
|
|
|
|
|
|
to contribute a patch that fixes this, please feel free; this is an open |
416
|
|
|
|
|
|
|
source project, after all ...) |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Bug the second: |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
The select-based server was originally written because I wanted to |
421
|
|
|
|
|
|
|
share state information between some forking servers and I couldn't |
422
|
|
|
|
|
|
|
use System V shared memory (the application had to be portable to a |
423
|
|
|
|
|
|
|
flavour of UNIX that didn't support it). |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
It works okay, up to a point, but under heavy load on Linux it can run |
426
|
|
|
|
|
|
|
into major problems. Partly this may be attributable to deficiencies |
427
|
|
|
|
|
|
|
in the way Linux handles the select() system call (or so Stephen |
428
|
|
|
|
|
|
|
Tweedie keeps telling me), but the result is that the select-based |
429
|
|
|
|
|
|
|
server tends to drop some connections when it's under stress: if |
430
|
|
|
|
|
|
|
two connections come in while it's serving another, the first may |
431
|
|
|
|
|
|
|
never get processed before a timeout occurs. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
A somewhat worse problem is that IO::Select doesn't do buffered (line- |
434
|
|
|
|
|
|
|
oriented) input; it just checks to see if one or more bytes are |
435
|
|
|
|
|
|
|
waiting to be read from one of the file handles it's got hold of. It |
436
|
|
|
|
|
|
|
is possible for a couple of bytes to come in (but not a whole line), |
437
|
|
|
|
|
|
|
so that the select-based server merrily tries to process a transaction |
438
|
|
|
|
|
|
|
and blocks until the rest of the input arrives -- thus ensuring that |
439
|
|
|
|
|
|
|
the server is bottlenecked by the speed of the slowest client connection. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Suggestion: if you need to serve lots of connections using select(), |
442
|
|
|
|
|
|
|
look at the eventserver module instead. If you're a bit more |
443
|
|
|
|
|
|
|
ambitious, the defect in NetServer::Generic is fixable by writing a |
444
|
|
|
|
|
|
|
module with a similar API to IO::Select, but which provides buffering |
445
|
|
|
|
|
|
|
for the file handles under its control and which only returns |
446
|
|
|
|
|
|
|
something in response to can_read() when one of the buffers has a |
447
|
|
|
|
|
|
|
complete line of input waiting. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head1 AUTHOR |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Charlie Stross (charle@antipope.org). With thanks for bugfixes and |
452
|
|
|
|
|
|
|
patches to Marius Kjeldahl I, Art Sackett |
453
|
|
|
|
|
|
|
I, Claudio Garcia I, |
454
|
|
|
|
|
|
|
Claudio Calvelli I, Martin Waite |
455
|
|
|
|
|
|
|
I. Debian package |
456
|
|
|
|
|
|
|
contributed by Jon Middleton, I. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head1 HISTORY |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=over 4 |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=item Version 0.1 |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Based on the simple forking server in Chapter 10 of "Advanced Perl |
465
|
|
|
|
|
|
|
Programming" by Sriram Srinivasan, with a modular wrapper to make |
466
|
|
|
|
|
|
|
it easy to use and configure, and a rudimentary access control system. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=item Version 0.2 |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Added the B method to provide peer information. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Bugfix to B from Marius Kjeldahl I. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Added select-based server code, B method to switch between forking |
475
|
|
|
|
|
|
|
and selection server modes. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Updated test code (should do something now!) |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Added example: fortune server and client code. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Supports NetServer::SMTP (and, internally, NetServer::vTID). |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=item Version 0.3 |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
fixed test failure. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=item Version 1.0 |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Added alpha-ish prefork server mode. |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Added alpha-ish multithreaded mode (UNSTABLE) |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Modified IP address filtering to cope with regexps |
494
|
|
|
|
|
|
|
(suggested by Art Sackett I) |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Modified select() server to do non-blocking writes via a |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Non-blocking-socket class tied to STDIN/STDOUT |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Option to log new connection peer addresses via STDERR |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Extra test scripts |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Updated documentation |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=item 1.01 |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Fix so it works on installations with no threading support (duh). Tested |
509
|
|
|
|
|
|
|
on Solaris, too. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item 1.02 |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
Bugfixes to the preforked mode (thanks to Art Sackett for detecting |
514
|
|
|
|
|
|
|
them). Bugfix to ok_to_serve() (thanks to Claudio Garcia, |
515
|
|
|
|
|
|
|
cgarcia@dbitech.com). Some notes on the two known bugs (related |
516
|
|
|
|
|
|
|
to buffering). |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item 1.03 |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
Signal handling code was fixed to avoid leaving zombie processes |
521
|
|
|
|
|
|
|
(thanks to Luis Munoz, lem@cantv.net) |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=back |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=cut |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# NetServer::FieldTypes contains a hash of autoload method names, and the |
529
|
|
|
|
|
|
|
# type of parameter they expect. For example, NetServer->callback() takes |
530
|
|
|
|
|
|
|
# a coderef as a parameter; AUTOLOAD needs to know this so it can whine |
531
|
|
|
|
|
|
|
# about incorrect parameter types. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
$NetServer::FieldTypes = { |
534
|
|
|
|
|
|
|
"port" => "scalar", |
535
|
|
|
|
|
|
|
"callback" => "code", |
536
|
|
|
|
|
|
|
"listen" => "scalar", |
537
|
|
|
|
|
|
|
"proto" => "scalar", |
538
|
|
|
|
|
|
|
"hostname" => "scalar", |
539
|
|
|
|
|
|
|
"timeout" => "scalar", |
540
|
|
|
|
|
|
|
"root_pid" => "scalar", |
541
|
|
|
|
|
|
|
"allowed" => "array", |
542
|
|
|
|
|
|
|
"forbidden" => "array", |
543
|
|
|
|
|
|
|
"peer" => "array", |
544
|
|
|
|
|
|
|
"mode" => "scalar", |
545
|
|
|
|
|
|
|
"trigger" => "code", |
546
|
|
|
|
|
|
|
"sock" => "IO::Socket::INET", |
547
|
|
|
|
|
|
|
"tags" => "hash", |
548
|
|
|
|
|
|
|
"my_age" => "scalar", |
549
|
|
|
|
|
|
|
"start_servers" => "scalar", |
550
|
|
|
|
|
|
|
"min_spare_servers" => "scalar", |
551
|
|
|
|
|
|
|
"max_servers" => "scalar", |
552
|
|
|
|
|
|
|
"server_lifespan" => "scalar", |
553
|
|
|
|
|
|
|
"fifo" => "scalar", |
554
|
|
|
|
|
|
|
"read_pipe" => "scalar", |
555
|
|
|
|
|
|
|
"write_pipe" => "scalar", |
556
|
|
|
|
|
|
|
"handle" => "IO::File", |
557
|
|
|
|
|
|
|
"scoreboard" => "hash", |
558
|
|
|
|
|
|
|
"servername" => "array", |
559
|
|
|
|
|
|
|
"parent_callback" => "code", |
560
|
|
|
|
|
|
|
"ante_parent_callback" => "code", |
561
|
|
|
|
|
|
|
}; |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# $NetServer::Debug; if non-zero, emit some debugging info on STDERR |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
$NetServer::Debug = 0; |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# here is a default callback routine. It basically echoes back anything |
568
|
|
|
|
|
|
|
# you sent to the server, unless the line begins with quit, bye, or |
569
|
|
|
|
|
|
|
# exit -- in which case it kills the server (rather than simply exiting). |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
$NetServer::default_cb = sub { |
572
|
|
|
|
|
|
|
my ($s) = shift; |
573
|
|
|
|
|
|
|
my ($tmp) = ""; |
574
|
|
|
|
|
|
|
print STDOUT "Echo server: type bye to quit, ", |
575
|
|
|
|
|
|
|
"exit to kill the server.\n\n" ; |
576
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
577
|
|
|
|
|
|
|
return if ($tmp =~ /^bye/i); |
578
|
|
|
|
|
|
|
$s->quit() if ($tmp =~ /^exit/i); |
579
|
|
|
|
|
|
|
print STDOUT "You said:>$tmp\n"; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
}; |
582
|
|
|
|
|
|
|
# Methods |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub new { |
585
|
28
|
50
|
|
28
|
0
|
259
|
$NetServer::Debug && print STDERR "[", join("][", @_), "]\n"; |
586
|
28
|
50
|
|
|
|
378
|
my ($class) = shift if @_; |
587
|
28
|
|
|
|
|
1066
|
my ($self) = {"listen" => 5, |
588
|
|
|
|
|
|
|
"timeout" => 60, |
589
|
|
|
|
|
|
|
"hostname" => "localhost", |
590
|
|
|
|
|
|
|
"proto" => "tcp", |
591
|
|
|
|
|
|
|
"callback" => $NetServer::default_cb, |
592
|
|
|
|
|
|
|
"version" => $NetServer::Generic::VERSION, |
593
|
|
|
|
|
|
|
}; |
594
|
28
|
|
|
|
|
226
|
$self->{tags} = $NetServer::FieldTypes; |
595
|
28
|
|
50
|
|
|
341
|
bless $self, ($class or "Server"); |
596
|
28
|
50
|
|
|
|
180
|
if (@_) { |
597
|
0
|
|
|
|
|
0
|
my (%tmp) = @_; my ($field) = ""; |
|
0
|
|
|
|
|
0
|
|
598
|
0
|
|
|
|
|
0
|
foreach $field (keys %tmp) { |
599
|
0
|
|
|
|
|
0
|
$self->$field($tmp{$field}); |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
} |
602
|
28
|
|
|
|
|
299
|
return $self; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub VERSION { |
606
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
607
|
0
|
|
|
|
|
0
|
return $self->{version}; |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub run_prefork { |
611
|
2
|
|
|
2
|
0
|
6
|
my $self = shift; |
612
|
|
|
|
|
|
|
# get preforking parameters or adopt sensible default values |
613
|
2
|
|
50
|
|
|
16
|
my $start_servers = ($self->start_servers() or 5 ); |
614
|
2
|
|
50
|
|
|
8
|
my $spare_servers = ($self->min_spare_servers() or 1 ); |
615
|
2
|
|
50
|
|
|
22
|
my $max_servers = ($self->max_servers() or 10 ); |
616
|
2
|
|
50
|
|
|
18
|
my $server_lifespan = ($self->server_lifespan() or 1000 ); |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
# Create socket and bind, then Fork repeatedly up to $start_servers times. |
619
|
|
|
|
|
|
|
# Once in each child, do a select-based loop. i.e. run_select, but exit |
620
|
|
|
|
|
|
|
# after handling $server_lifespan transactions. |
621
|
|
|
|
|
|
|
# Every time we do a task we write our PID and generation number down a |
622
|
|
|
|
|
|
|
# pipe to the parent process, with a message when we exit. |
623
|
|
|
|
|
|
|
# |
624
|
|
|
|
|
|
|
# In the parent, keep track of how many servers are in use |
625
|
|
|
|
|
|
|
# and fire up extra children (up to $max_servers) if the number in |
626
|
|
|
|
|
|
|
# use leaves less than $spare_servers free. |
627
|
2
|
|
|
|
|
8
|
my %init = ( |
628
|
|
|
|
|
|
|
LocalPort => $self->port(), |
629
|
|
|
|
|
|
|
Listen => $self->listen(), |
630
|
|
|
|
|
|
|
Proto => $self->proto(), |
631
|
|
|
|
|
|
|
Reuse => 1 |
632
|
|
|
|
|
|
|
); |
633
|
2
|
50
|
|
|
|
10
|
if ($self->hostname() ne "") { |
634
|
2
|
|
|
|
|
8
|
$init{LocalAddr} = $self->hostname(); |
635
|
|
|
|
|
|
|
} |
636
|
2
|
|
|
|
|
102
|
my ($main_sock) = new IO::Socket::INET(%init); |
637
|
2
|
50
|
|
|
|
3070
|
if (! $main_sock) { |
638
|
0
|
|
|
|
|
0
|
print STDERR "$$:run_select(): could not create socket: $!\n"; |
639
|
0
|
|
|
|
|
0
|
exit 0; |
640
|
|
|
|
|
|
|
} |
641
|
2
|
|
|
|
|
20
|
$self->sock($main_sock); |
642
|
2
|
50
|
|
|
|
8
|
$NetServer::Debug && print STDERR |
643
|
|
|
|
|
|
|
"Created socket(port => ", $self->port(), "\n", |
644
|
|
|
|
|
|
|
" " x 15, "hostname => ", $self->hostname(), ")\n"; |
645
|
2
|
|
|
|
|
18
|
my $scoreboard = {}; |
646
|
2
|
|
|
|
|
34
|
$self->scoreboard($scoreboard); |
647
|
|
|
|
|
|
|
# set up named pipe -- children will write, parent will read |
648
|
|
|
|
|
|
|
#my $fifo = $self->_new_fifo(); |
649
|
|
|
|
|
|
|
#$self->fifo($fifo); |
650
|
|
|
|
|
|
|
# switch to using a pipe instead |
651
|
2
|
|
|
|
|
26
|
pipe(READ_PIPE, WRITE_PIPE); |
652
|
2
|
|
|
|
|
12
|
$self->{read_pipe} = *READ_PIPE; |
653
|
2
|
|
|
|
|
14
|
$self->{write_pipe} = *WRITE_PIPE; |
654
|
2
|
|
|
|
|
20
|
$self->root_pid($$); # set server root PID |
655
|
|
|
|
|
|
|
# now create lots of spawn |
656
|
2
|
|
|
|
|
22
|
for (my $i = 0; $i < $start_servers; $i++) { |
657
|
3
|
|
|
|
|
4267
|
my $pid = fork(); |
658
|
3
|
50
|
|
|
|
278
|
die "Cannot fork: $!\n" unless defined ($pid); |
659
|
3
|
100
|
|
|
|
107
|
if ($pid == 0) { |
660
|
|
|
|
|
|
|
# child |
661
|
2
|
|
|
|
|
271
|
$self->_do_preforked_child(); |
662
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: end of transaction\n"; |
663
|
0
|
|
|
|
|
0
|
exit 0; |
664
|
|
|
|
|
|
|
} else { |
665
|
|
|
|
|
|
|
# parent |
666
|
1
|
|
|
|
|
60
|
$scoreboard->{$pid} = "idle"; |
667
|
1
|
50
|
|
|
|
17
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
} |
670
|
|
|
|
|
|
|
# we have no forked $start_servers children that are |
671
|
|
|
|
|
|
|
# in _do_preforked_child(). |
672
|
0
|
|
|
|
|
0
|
$self->scoreboard($scoreboard); |
673
|
0
|
|
|
|
|
0
|
$self->_do_preforked_parent(); |
674
|
0
|
|
|
|
|
0
|
return; |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
sub reap_child { |
678
|
2
|
|
|
2
|
0
|
114
|
do {} while waitpid(-1, WNOHANG) > 0; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
sub _do_preforked_parent { |
682
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
683
|
|
|
|
|
|
|
# we are a parent process to a bunch of raucous kiddies. We have an |
684
|
|
|
|
|
|
|
# IO::Pipe called $self->reader() that we read status from and stick |
685
|
|
|
|
|
|
|
# in a scoreboard. As processes die, we replace them. As the scoreboard |
686
|
|
|
|
|
|
|
# fills up, we add extra servers. NB: when we fork, we replicate |
687
|
|
|
|
|
|
|
# self->reader() and self->writer(). |
688
|
|
|
|
|
|
|
|
689
|
0
|
|
|
|
|
0
|
my $n = "_do_preforked_adult($$)"; # for reporting status |
690
|
0
|
|
0
|
|
|
0
|
my $start_servers = ( $self->start_servers() or 5 ); |
691
|
0
|
|
0
|
|
|
0
|
my $spare_servers = ( $self->min_spare_servers() or 1 ); |
692
|
0
|
|
0
|
|
|
0
|
my $max_servers = ( $self->max_servers() or 10 ); |
693
|
0
|
|
0
|
|
|
0
|
my $scoreboard = ( $self->scoreboard() or {} ); |
694
|
0
|
|
|
|
|
0
|
$SIG{CHLD} = \&reap_child; |
695
|
0
|
|
|
|
|
0
|
my @buffer = (); |
696
|
0
|
|
|
|
|
0
|
my $buffer = ""; |
697
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$n: About to loop on scoreboard file\n"; |
698
|
0
|
|
|
|
|
0
|
my $loopcnt = 0; |
699
|
0
|
|
|
|
|
0
|
my $busycnt = 0; |
700
|
0
|
|
|
|
|
0
|
my @busyvec = (); |
701
|
|
|
|
|
|
|
#while(@buffer = $self->_read_fifo()) { |
702
|
0
|
|
|
|
|
0
|
*READ_PIPE = $self->read_pipe(); |
703
|
0
|
|
|
|
|
0
|
while($buffer = ) { |
704
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
705
|
|
|
|
|
|
|
&& print STDERR "busyvec: [", join("][", @busyvec), "]\n"; |
706
|
0
|
|
|
|
|
0
|
$loopcnt++; |
707
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
708
|
|
|
|
|
|
|
&& print STDERR "$n: in pipe read loop $loopcnt\n"; |
709
|
0
|
|
|
|
|
0
|
$buffer =~ tr/ //; |
710
|
0
|
|
|
|
|
0
|
chomp $buffer; |
711
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
712
|
|
|
|
|
|
|
&& print STDERR "$n: buffer: $buffer\n"; |
713
|
0
|
|
|
|
|
0
|
my ($child_pid, $status) = split(/:/, $buffer); |
714
|
|
|
|
|
|
|
# kids write $$:busy or $$:idle into the pipe whenever |
715
|
|
|
|
|
|
|
# they change state. |
716
|
0
|
0
|
|
|
|
0
|
if ($status eq "exit") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# a child just exited on us |
718
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
719
|
|
|
|
|
|
|
&& print STDERR "$n: child $child_pid just died\n"; |
720
|
0
|
|
|
|
|
0
|
delete($scoreboard->{$child_pid}); |
721
|
|
|
|
|
|
|
} elsif ($status eq "busy") { |
722
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "busy"; |
723
|
0
|
|
|
|
|
0
|
push(@busyvec, $child_pid); |
724
|
0
|
|
|
|
|
0
|
$busycnt++; |
725
|
|
|
|
|
|
|
} elsif ($status eq "idle") { |
726
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "idle"; |
727
|
0
|
|
|
|
|
0
|
@busyvec = grep(!/$child_pid/, @busyvec); |
728
|
0
|
|
|
|
|
0
|
$busycnt--; |
729
|
|
|
|
|
|
|
} elsif ($status eq "start") { |
730
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "idle"; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
$NetServer::Debug && print STDERR "$n: $child_pid has status [", |
733
|
0
|
0
|
|
|
|
0
|
$scoreboard->{$child_pid}, "]\n", |
734
|
|
|
|
|
|
|
"$n: got ", scalar(@busyvec), " busy kids\n"; |
735
|
0
|
|
|
|
|
0
|
$busycnt = scalar(@busyvec); |
736
|
0
|
|
|
|
|
0
|
my $all_kids = scalar keys %$scoreboard; |
737
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
738
|
|
|
|
|
|
|
print STDERR "$n: $busycnt children busy of $all_kids total\n"; |
739
|
|
|
|
|
|
|
# busy_kids is number of kids currently busy; all_kids is number of kids |
740
|
0
|
0
|
0
|
|
|
0
|
if ((($all_kids - $busycnt) < $spare_servers) and |
741
|
|
|
|
|
|
|
($all_kids <= $max_servers)) { |
742
|
0
|
|
|
|
|
0
|
my $kids_to_launch = ($spare_servers - ($all_kids - $busycnt)) +1; |
743
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
744
|
|
|
|
|
|
|
print STDERR "spare servers: $spare_servers, ", |
745
|
|
|
|
|
|
|
"all kids: $all_kids, ", |
746
|
|
|
|
|
|
|
"busycnt: $busycnt\n", |
747
|
|
|
|
|
|
|
"kids to launch = spares - (all - busy) +1 ", |
748
|
|
|
|
|
|
|
" => $kids_to_launch\n"; |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# launch new children |
751
|
0
|
|
|
|
|
0
|
for (my ($i) = 0; $i < $kids_to_launch; $i++) { |
752
|
0
|
|
|
|
|
0
|
my $pid = fork(); |
753
|
0
|
0
|
|
|
|
0
|
if ($pid == 0) { |
754
|
|
|
|
|
|
|
# new child |
755
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
756
|
|
|
|
|
|
|
print STDERR "spawned child\n"; |
757
|
0
|
|
|
|
|
0
|
$self->_do_preforked_child(); |
758
|
0
|
|
|
|
|
0
|
exit 0; |
759
|
|
|
|
|
|
|
} else { |
760
|
|
|
|
|
|
|
# parent |
761
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
762
|
|
|
|
|
|
|
"$n: spawned new child $pid\n"; |
763
|
0
|
|
|
|
|
0
|
$scoreboard->{$pid} = "idle"; |
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
} |
766
|
|
|
|
|
|
|
} # end of child launch cycle |
767
|
|
|
|
|
|
|
$NetServer::Debug |
768
|
0
|
0
|
|
|
|
0
|
&& print STDERR "$n: scoreboard: \n", Dumper $scoreboard; |
769
|
|
|
|
|
|
|
} |
770
|
0
|
|
|
|
|
0
|
print STDERR "exited getline loop\n"; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
sub _do_preforked_child { |
774
|
2
|
|
|
2
|
|
42
|
my $self = shift; |
775
|
|
|
|
|
|
|
# we are a preforked child process. We have an IO::Pipe called |
776
|
|
|
|
|
|
|
# $self->writer() that we write strange things to. Each "strange thing" |
777
|
|
|
|
|
|
|
# consists of a line containing our PID, a colon, and one of three strings: |
778
|
|
|
|
|
|
|
# busy, idle, or exit. We run like a run_select server, except that we |
779
|
|
|
|
|
|
|
# write a busy line whenever we accept a connection, an idle line whenever |
780
|
|
|
|
|
|
|
# we finish handling a connection, and an exit line when our age exceeds |
781
|
|
|
|
|
|
|
# $self->server_lifespan() and we suicide. |
782
|
|
|
|
|
|
|
# |
783
|
2
|
|
|
|
|
144
|
my $n = "_do_preforked_child($$)"; # for reporting status |
784
|
2
|
|
50
|
|
|
351
|
my $server_lifespan = ( $self->server_lifespan() or 1000 ); |
785
|
2
|
|
50
|
|
|
154
|
my $my_age = ( $self->my_age() or 0 ); |
786
|
2
|
|
|
|
|
27
|
my $main_sock = $self->sock(); |
787
|
2
|
|
|
|
|
17
|
my $LOCK_SH = 1; |
788
|
2
|
|
|
|
|
19
|
my $LOCK_EX = 2; |
789
|
2
|
|
|
|
|
9
|
my $LOCK_NB = 4; |
790
|
2
|
|
|
|
|
16
|
my $LOCK_UN = 8; |
791
|
2
|
|
|
|
|
276
|
my $rh = new IO::Select($main_sock); |
792
|
2
|
50
|
|
|
|
942
|
$NetServer::Debug && print STDERR "$n: Created IO::Select()\n"; |
793
|
2
|
|
|
|
|
96
|
*WRITE_PIPE = $self->{write_pipe}; |
794
|
2
|
50
|
|
|
|
17
|
$NetServer::Debug |
795
|
|
|
|
|
|
|
&& print WRITE_PIPE "$$:start\n"; |
796
|
2
|
|
|
|
|
31
|
my (@ready, @err) = (); |
797
|
2
|
50
|
|
|
|
30
|
$NetServer::Debug |
798
|
|
|
|
|
|
|
&& print STDERR "$n: about to call IO::Select->can_read()\n"; |
799
|
|
|
|
|
|
|
SELECT: |
800
|
2
|
|
33
|
|
|
140
|
while (@ready = $rh->can_read() or @err = $rh->has_error(0)) { |
801
|
8
|
50
|
|
|
|
6376
|
if (scalar(@err) > 0) { |
802
|
0
|
|
|
|
|
0
|
foreach my $s (@err) { |
803
|
0
|
0
|
|
|
|
0
|
if ($NetServer::Debug > 0) { |
804
|
0
|
|
|
|
|
0
|
print STDERR "Sock err: ", $s->error(), "\n"; |
805
|
|
|
|
|
|
|
} |
806
|
0
|
0
|
|
|
|
0
|
if ($s->eof()) { |
807
|
0
|
|
|
|
|
0
|
$rh->remove($s); |
808
|
0
|
|
|
|
|
0
|
$s->close(); |
809
|
|
|
|
|
|
|
} else { |
810
|
0
|
|
|
|
|
0
|
$s->clearerr(); |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
} |
813
|
0
|
|
|
|
|
0
|
@err = (); |
814
|
0
|
|
|
|
|
0
|
next SELECT; |
815
|
|
|
|
|
|
|
} |
816
|
8
|
50
|
|
|
|
55
|
$NetServer::Debug && print STDERR "$n: got a connection\n"; |
817
|
8
|
|
|
|
|
36
|
foreach my $sock (@ready) { |
818
|
9
|
50
|
|
|
|
26
|
$NetServer::Debug && print STDERR "$n: got a socket\n"; |
819
|
9
|
100
|
|
|
|
48
|
if ($sock == $main_sock) { |
820
|
5
|
50
|
|
|
|
98
|
flock($sock, $LOCK_EX) or do { |
821
|
0
|
|
|
|
|
0
|
print STDERR "+++ flock LOCK_EX failed on parent socket: ", |
822
|
|
|
|
|
|
|
"$!\n"; |
823
|
|
|
|
|
|
|
}; |
824
|
5
|
|
|
|
|
256
|
my ($new_sock) = $sock->accept(); |
825
|
5
|
|
|
|
|
1968
|
flock $sock, $LOCK_UN; |
826
|
5
|
|
|
|
|
23
|
$new_sock->autoflush(1); |
827
|
5
|
|
|
|
|
193
|
$rh->add($new_sock); |
828
|
5
|
50
|
|
|
|
248
|
if (! $self->ok_to_serve($new_sock)) { |
829
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
830
|
0
|
|
|
|
|
0
|
close($sock); |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
} else { |
833
|
4
|
50
|
|
|
|
127
|
if (! eof($sock)) { |
834
|
4
|
|
|
|
|
14
|
$my_age++; |
835
|
4
|
50
|
|
|
|
20
|
$NetServer::Debug |
836
|
|
|
|
|
|
|
&& print STDERR "$n: print WRITE_PIPE ($$:busy)\n"; |
837
|
4
|
|
|
|
|
86
|
print WRITE_PIPE "$$:busy\n"; |
838
|
4
|
50
|
|
|
|
18
|
$NetServer::Debug |
839
|
|
|
|
|
|
|
&& print STDERR "$n: serving connection\n"; |
840
|
4
|
|
|
|
|
24
|
$sock->autoflush(1); |
841
|
4
|
|
|
|
|
234
|
my ($in_port, $in_addr) = sockaddr_in($sock->sockname()); |
842
|
4
|
|
|
|
|
169
|
$self->servername([$in_port, $in_addr]); |
843
|
4
|
|
|
|
|
30
|
my ($code) = $self->callback(); |
844
|
4
|
|
|
|
|
39
|
$self->sock($sock); |
845
|
4
|
|
|
|
|
93
|
*OLD_STDIN = *STDIN; |
846
|
4
|
|
|
|
|
35
|
*OLD_STDOUT = *STDOUT; |
847
|
4
|
|
|
|
|
12
|
*STDIN = $sock; |
848
|
4
|
|
|
|
|
13
|
*STDOUT = $sock; |
849
|
4
|
|
|
|
|
13
|
select STDIN; $| = 1; |
|
4
|
|
|
|
|
51
|
|
850
|
4
|
|
|
|
|
194
|
select STDOUT; $| = 1; |
|
4
|
|
|
|
|
11
|
|
851
|
4
|
|
|
|
|
265
|
&$code($self); |
852
|
3
|
|
|
|
|
30
|
*STDIN = *OLD_STDIN; |
853
|
3
|
|
|
|
|
16
|
*STDOUT = *OLD_STDOUT; |
854
|
3
|
50
|
|
|
|
19
|
$NetServer::Debug && do { |
855
|
0
|
|
|
|
|
0
|
print STDERR "$n: print WRITE_PIPE $$:idle\n", |
856
|
|
|
|
|
|
|
"$n: served $my_age calls\n"; |
857
|
|
|
|
|
|
|
}; |
858
|
3
|
|
|
|
|
38
|
print WRITE_PIPE "$$:idle\n$$:idle\n"; |
859
|
3
|
|
|
|
|
33
|
$rh->remove($sock); |
860
|
3
|
|
|
|
|
562
|
close $sock; |
861
|
|
|
|
|
|
|
} else { |
862
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
863
|
0
|
|
|
|
|
0
|
close($sock); |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
} |
867
|
7
|
50
|
|
|
|
28
|
$NetServer::Debug && print STDERR "$n: checking age $my_age ", |
868
|
|
|
|
|
|
|
"against lifespan $server_lifespan\n"; |
869
|
7
|
100
|
|
|
|
58
|
if ($my_age >= $server_lifespan) { |
870
|
1
|
50
|
|
|
|
6
|
$NetServer::Debug |
871
|
|
|
|
|
|
|
&& print STDERR "$n: time to live exceeded\n", |
872
|
|
|
|
|
|
|
"$n: print WRITE_PIPE $$:exit\n"; |
873
|
|
|
|
|
|
|
#$self->_write_fifo("$$:exit\n"); |
874
|
1
|
|
|
|
|
16
|
print WRITE_PIPE "$$:exit\n"; |
875
|
1
|
|
|
|
|
446
|
exit 0; |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
$NetServer::Debug |
879
|
0
|
0
|
|
|
|
0
|
&& print STDERR "Warning! Should never reach this point:", |
880
|
|
|
|
|
|
|
join("\n", caller()), "\n"; |
881
|
0
|
|
|
|
|
0
|
print WRITE_PIPE "$$:exit\n"; |
882
|
0
|
|
|
|
|
0
|
exit 0; |
883
|
|
|
|
|
|
|
} |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub run_select { |
887
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
888
|
0
|
|
|
|
|
0
|
my ($main_sock) = |
889
|
|
|
|
|
|
|
new IO::Socket::INET( # LocalAddr => $self->hostname(), |
890
|
|
|
|
|
|
|
LocalPort => $self->port(), |
891
|
|
|
|
|
|
|
Listen => $self->listen(), |
892
|
|
|
|
|
|
|
Proto => $self->proto(), |
893
|
|
|
|
|
|
|
Reuse => 1 |
894
|
|
|
|
|
|
|
); |
895
|
|
|
|
|
|
|
# die "$$:run_select(): could not create socket: $!\n" unless ($main_sock); |
896
|
0
|
0
|
|
|
|
0
|
if (! $main_sock) { |
897
|
0
|
|
|
|
|
0
|
print STDERR "$$:run_select(): could not create socket: $!\n"; |
898
|
0
|
|
|
|
|
0
|
exit 0; |
899
|
|
|
|
|
|
|
} |
900
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created socket\n"; |
901
|
0
|
|
|
|
|
0
|
my $rh = new IO::Select($main_sock); |
902
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created IO::Select()\n"; |
903
|
0
|
|
|
|
|
0
|
my (@ready) = (); |
904
|
0
|
|
|
|
|
0
|
while (@ready = $rh->can_read() ) { |
905
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
906
|
|
|
|
|
|
|
"NetServer::Generic::run_select(): got ", |
907
|
|
|
|
|
|
|
scalar(@ready), " handles at ", |
908
|
|
|
|
|
|
|
scalar(localtime(time)), "\n"; |
909
|
0
|
|
|
|
|
0
|
my ($sock) = ""; |
910
|
0
|
|
|
|
|
0
|
foreach $sock (@ready) { |
911
|
0
|
0
|
|
|
|
0
|
if ($sock == $main_sock) { |
912
|
0
|
|
|
|
|
0
|
my ($new_sock) = $sock->accept(); |
913
|
0
|
|
|
|
|
0
|
$new_sock->autoflush(1); |
914
|
0
|
|
|
|
|
0
|
$rh->add($new_sock); |
915
|
0
|
0
|
|
|
|
0
|
if (! $self->ok_to_serve($new_sock)) { |
916
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
917
|
0
|
|
|
|
|
0
|
close($sock); |
918
|
|
|
|
|
|
|
} |
919
|
|
|
|
|
|
|
} else { |
920
|
0
|
0
|
|
|
|
0
|
if (! eof($sock)) { |
921
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
922
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
923
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
924
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
925
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
926
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
0
|
|
|
|
|
0
|
|
927
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
0
|
|
|
|
|
0
|
|
928
|
0
|
|
|
|
|
0
|
&$code($self); |
929
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
930
|
0
|
|
|
|
|
0
|
close $sock; |
931
|
|
|
|
|
|
|
# shutdown($sock, 2); |
932
|
|
|
|
|
|
|
} else { |
933
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
934
|
0
|
|
|
|
|
0
|
close($sock); |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
} |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
sub run_thread { |
942
|
|
|
|
|
|
|
# first pass at multithreaded execution -- as for fork() except we use |
943
|
|
|
|
|
|
|
# threads. This is ugly -- may want to bodge it up to see if the |
944
|
|
|
|
|
|
|
# run_select_fast method is a better model? |
945
|
0
|
|
|
0
|
0
|
0
|
my ($self) = shift ; |
946
|
0
|
0
|
|
|
|
0
|
if ($MAIN::no_thread == 1) { |
947
|
0
|
|
|
|
|
0
|
warn "Warning: Threading not supported!\n"; |
948
|
0
|
|
|
|
|
0
|
return; |
949
|
|
|
|
|
|
|
} |
950
|
0
|
|
|
|
|
0
|
my %init = ( |
951
|
|
|
|
|
|
|
LocalPort => $self->port(), |
952
|
|
|
|
|
|
|
Listen => $self->listen(), |
953
|
|
|
|
|
|
|
Proto => $self->proto(), |
954
|
|
|
|
|
|
|
Reuse => 1 |
955
|
|
|
|
|
|
|
); |
956
|
0
|
0
|
|
|
|
0
|
if ($self->hostname() ne "") { |
957
|
0
|
|
|
|
|
0
|
$init{LocalAddr} = $self->hostname(); |
958
|
|
|
|
|
|
|
} |
959
|
0
|
|
|
|
|
0
|
my ($main_sock) = new IO::Socket::INET(%init); |
960
|
|
|
|
|
|
|
|
961
|
0
|
0
|
|
|
|
0
|
die "Socket could not be created: $!\n" unless ($main_sock); |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# we need to trap SIGKILL and SIGINT. If no traps are already |
964
|
|
|
|
|
|
|
# defined by the user, add some default ones. |
965
|
0
|
0
|
|
|
|
0
|
if (! exists $SIG{INT}) { |
966
|
|
|
|
|
|
|
$SIG{INT} = sub { |
967
|
0
|
|
|
0
|
|
0
|
print STDERR "\nSIGINT: server $$ ", |
968
|
|
|
|
|
|
|
"shutting down \n"; |
969
|
0
|
|
|
|
|
0
|
exit 0; |
970
|
0
|
|
|
|
|
0
|
}; |
971
|
|
|
|
|
|
|
} |
972
|
|
|
|
|
|
|
# and make sure we wait() on children |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# now loop, forking whenever a new connection arrives on the listener |
975
|
|
|
|
|
|
|
|
976
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created socket\n"; |
977
|
0
|
|
|
|
|
0
|
my $rh = new IO::Select($main_sock); |
978
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created IO::Select()\n"; |
979
|
0
|
|
|
|
|
0
|
my (@ready) = (); |
980
|
0
|
|
|
|
|
0
|
while (@ready = $rh->can_read()) { |
981
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
982
|
|
|
|
|
|
|
"NetServer::Generic::run_select(): got ", |
983
|
|
|
|
|
|
|
scalar(@ready), " handles at ", scalar(localtime(time)), "\n"; |
984
|
0
|
|
|
|
|
0
|
my ($sock) = ""; |
985
|
0
|
|
|
|
|
0
|
foreach $sock (@ready) { |
986
|
0
|
0
|
|
|
|
0
|
if ($sock == $main_sock) { |
987
|
0
|
|
|
|
|
0
|
my ($new_sock) = $sock->accept(); |
988
|
0
|
|
|
|
|
0
|
$new_sock->autoflush(1); |
989
|
0
|
|
|
|
|
0
|
$rh->add($new_sock); |
990
|
0
|
0
|
|
|
|
0
|
if (! $self->ok_to_serve($new_sock)) { |
991
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
992
|
0
|
|
|
|
|
0
|
close($sock); |
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
} else { |
995
|
0
|
0
|
|
|
|
0
|
if (! eof($sock)) { |
996
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
997
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
998
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
999
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
1000
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
1001
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
0
|
|
|
|
|
0
|
|
1002
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
0
|
|
|
|
|
0
|
|
1003
|
0
|
|
|
|
|
0
|
my $t = new Thread &$code($self) ; |
1004
|
0
|
|
|
|
|
0
|
$t->detach(); |
1005
|
|
|
|
|
|
|
#&$code($self); |
1006
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
1007
|
0
|
|
|
|
|
0
|
close $sock; |
1008
|
|
|
|
|
|
|
# shutdown($sock, 2); |
1009
|
|
|
|
|
|
|
} else { |
1010
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
1011
|
0
|
|
|
|
|
0
|
close($sock); |
1012
|
|
|
|
|
|
|
} |
1013
|
|
|
|
|
|
|
} |
1014
|
|
|
|
|
|
|
} |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
sub _thread { |
1019
|
|
|
|
|
|
|
# handle socket setup inside a thread |
1020
|
|
|
|
|
|
|
# args: IO::Socket::INET object, NetServer::Generic object |
1021
|
0
|
|
|
0
|
|
0
|
my $sock = shift; |
1022
|
0
|
|
|
|
|
0
|
my $self = shift; |
1023
|
0
|
|
0
|
|
|
0
|
print STDERR "self is a ", (ref($self) or " kangaroo "), "\n"; |
1024
|
0
|
0
|
|
|
|
0
|
if ($self->ok_to_serve($sock)) { |
1025
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
1026
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
1027
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
1028
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
1029
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
0
|
|
|
|
|
0
|
|
1030
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
0
|
|
|
|
|
0
|
|
1031
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
1032
|
0
|
|
|
|
|
0
|
&$code($self); |
1033
|
|
|
|
|
|
|
} |
1034
|
0
|
|
|
|
|
0
|
shutdown($sock, 2); |
1035
|
0
|
|
|
|
|
0
|
return; |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub run_fork { |
1039
|
1
|
|
|
1
|
0
|
2
|
my ($self) = shift ; |
1040
|
1
|
|
|
|
|
10
|
my %init = ( |
1041
|
|
|
|
|
|
|
LocalPort => $self->port(), |
1042
|
|
|
|
|
|
|
Listen => $self->listen(), |
1043
|
|
|
|
|
|
|
Proto => $self->proto(), |
1044
|
|
|
|
|
|
|
Reuse => 1 |
1045
|
|
|
|
|
|
|
); |
1046
|
1
|
50
|
|
|
|
10
|
if ($self->hostname() ne "") { |
1047
|
1
|
|
|
|
|
3
|
$init{LocalAddr} = $self->hostname(); |
1048
|
|
|
|
|
|
|
} |
1049
|
1
|
|
|
|
|
68
|
my ($main_sock) = new IO::Socket::INET(%init); |
1050
|
|
|
|
|
|
|
|
1051
|
1
|
50
|
|
|
|
381859
|
die "Socket could not be created: $!\n" unless ($main_sock); |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
# we need to trap SIGKILL and SIGINT. If no traps are already |
1055
|
|
|
|
|
|
|
# defined by the user, add some default ones. |
1056
|
1
|
50
|
|
|
|
13
|
if (! exists $SIG{INT}) { |
1057
|
|
|
|
|
|
|
$SIG{INT} = sub { |
1058
|
0
|
|
|
0
|
|
0
|
print STDERR "\nSIGINT: server $$ ", |
1059
|
|
|
|
|
|
|
"shutting down \n"; |
1060
|
0
|
|
|
|
|
0
|
exit 0; |
1061
|
0
|
|
|
|
|
0
|
}; |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
# and make sure we wait() on children |
1064
|
1
|
|
|
|
|
28
|
$SIG{CHLD} = \&reap_child; |
1065
|
1
|
|
|
|
|
47
|
my $parent_callback = $self->parent_callback(); |
1066
|
1
|
|
|
|
|
29
|
my $ante_fork_callback = $self->ante_fork_callback(); |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# now loop, forking whenever a new connection arrives on the listener |
1069
|
1
|
|
|
|
|
23
|
$self->root_pid($$); # set server root PID |
1070
|
1
|
|
|
|
|
20
|
while (my ($new_sock) = $main_sock->accept()) { |
1071
|
1
|
50
|
|
|
|
314
|
&$ante_fork_callback($self) if ( defined $ante_fork_callback ); |
1072
|
1
|
|
|
|
|
26
|
my $x_time = [ gettimeofday ]; # millisecond timer to track duration |
1073
|
1
|
|
|
|
|
2434
|
my $pid = fork(); |
1074
|
1
|
50
|
|
|
|
88
|
die "Cannot fork: $!\n" unless defined ($pid); |
1075
|
1
|
50
|
|
|
|
40
|
if ($pid == 0) { |
1076
|
|
|
|
|
|
|
# child |
1077
|
1
|
50
|
|
|
|
43
|
if ($NetServer::Debug != 0) { |
1078
|
0
|
|
|
|
|
0
|
my ($peeraddr) = join(".", unpack("C4", $new_sock->peeraddr())); |
1079
|
0
|
|
|
|
|
0
|
print STDERR "$$ : ", scalar(localtime(time)), " : ", |
1080
|
|
|
|
|
|
|
"incoming connection from $peeraddr\n"; |
1081
|
|
|
|
|
|
|
} |
1082
|
1
|
50
|
|
|
|
229
|
if ($self->ok_to_serve($new_sock)) { |
1083
|
1
|
50
|
|
|
|
24
|
$NetServer::Debug |
1084
|
|
|
|
|
|
|
&& print STDERR $$, " : ", scalar(localtime(time)), " : ", |
1085
|
|
|
|
|
|
|
"processing connection\n"; |
1086
|
1
|
|
|
|
|
28
|
$new_sock->autoflush(1); |
1087
|
1
|
|
|
|
|
202
|
my ($code) = $self->callback(); |
1088
|
1
|
|
|
|
|
28
|
*STDIN = $new_sock; |
1089
|
1
|
|
|
|
|
8
|
*STDOUT = $new_sock; |
1090
|
1
|
|
|
|
|
6
|
select STDIN; $| = 1; |
|
1
|
|
|
|
|
4
|
|
1091
|
1
|
|
|
|
|
6
|
select STDOUT; $| = 1; |
|
1
|
|
|
|
|
5
|
|
1092
|
1
|
|
|
|
|
45
|
$self->sock($new_sock); |
1093
|
1
|
|
|
|
|
32
|
&$code($self); |
1094
|
|
|
|
|
|
|
} else { |
1095
|
0
|
0
|
|
|
|
0
|
if ($NetServer::Debug) { |
1096
|
0
|
|
|
|
|
0
|
print STDERR $$, " : ", scalar(localtime(time)), " : ", |
1097
|
|
|
|
|
|
|
"rejecting unauthed connection\n"; |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
} |
1100
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: end of transaction\n"; |
1101
|
0
|
|
|
|
|
0
|
shutdown($new_sock, 2); |
1102
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR $$, " : ", |
1103
|
|
|
|
|
|
|
scalar(localtime(time)), " : ", |
1104
|
|
|
|
|
|
|
"took ", tv_interval($x_time), |
1105
|
|
|
|
|
|
|
" seconds\n"; |
1106
|
0
|
|
|
|
|
0
|
exit 0; |
1107
|
|
|
|
|
|
|
} else { |
1108
|
|
|
|
|
|
|
# parent |
1109
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
1110
|
0
|
0
|
|
|
|
0
|
if ( defined $parent_callback ) { |
1111
|
0
|
|
|
|
|
0
|
&$parent_callback($self); |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
} |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
sub run_client { |
1118
|
11
|
|
|
11
|
0
|
80
|
my ($self) = shift ; |
1119
|
11
|
|
|
|
|
1698
|
$SIG{CHLD} = \&reap_child; |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
# despatcher is a routine that dictates how often and how fast the |
1122
|
|
|
|
|
|
|
# server forks and execs the test callback. The default sub (below) |
1123
|
|
|
|
|
|
|
# returns immediately but is only true once, so the test is executed |
1124
|
|
|
|
|
|
|
# immediately one time only. More realistic despatchers may sleep for |
1125
|
|
|
|
|
|
|
# a random interval or even pre-fork themselves (for added chaos). |
1126
|
|
|
|
|
|
|
my $despatcher = $self->trigger() || |
1127
|
0
|
|
|
0
|
|
0
|
sub { $NetServer::Generic::default_trigger++; |
1128
|
0
|
0
|
|
|
|
0
|
return(($NetServer::Generic::default_trigger > 1) ? 0 : 1 ); |
1129
|
11
|
|
50
|
|
|
190
|
}; |
1130
|
|
|
|
|
|
|
|
1131
|
11
|
|
|
|
|
142
|
my $code = $self->callback(); # sub to call in child process |
1132
|
11
|
|
|
|
|
392
|
$self->root_pid($$); # set server root PID |
1133
|
11
|
|
|
|
|
133
|
my $triggerval = &$despatcher; |
1134
|
11
|
|
66
|
|
|
375
|
while (($triggerval ne "") && ($triggerval ne "0")) { |
1135
|
|
|
|
|
|
|
# loop, forking to create new client sessions |
1136
|
34
|
|
|
|
|
57494
|
my $pid = fork(); |
1137
|
34
|
50
|
|
|
|
1544
|
die "Cannot fork: $!\n" unless defined ($pid); |
1138
|
34
|
100
|
|
|
|
2016
|
if ($pid == 0) { |
1139
|
|
|
|
|
|
|
# child |
1140
|
9
|
50
|
|
|
|
837
|
if ($NetServer::Debug != 0) { |
1141
|
0
|
|
|
|
|
0
|
print STDERR "[$$] about to call new ", |
1142
|
|
|
|
|
|
|
"IO::Socket::INET(\n\t\t\t\t", |
1143
|
|
|
|
|
|
|
"PeerAddr => ", $self->hostname(), |
1144
|
|
|
|
|
|
|
"\n\t\t\t\tPeerPort => ", $self->port(), |
1145
|
|
|
|
|
|
|
"\n\t\t\t\tProto => ", $self->proto(), |
1146
|
|
|
|
|
|
|
"\n)\n"; |
1147
|
|
|
|
|
|
|
} |
1148
|
9
|
|
|
|
|
1844
|
my ($sock) = |
1149
|
|
|
|
|
|
|
new IO::Socket::INET( PeerAddr => $self->hostname(), |
1150
|
|
|
|
|
|
|
PeerPort => $self->port(), |
1151
|
|
|
|
|
|
|
Proto => $self->proto(), |
1152
|
|
|
|
|
|
|
); |
1153
|
9
|
100
|
|
|
|
1515909
|
die "Socket could not be created: $!\n" unless ($sock); |
1154
|
6
|
|
|
|
|
85
|
*STDIN = $sock; |
1155
|
6
|
|
|
|
|
23
|
*STDOUT = $sock; |
1156
|
6
|
|
|
|
|
30
|
select STDIN; $| = 1; |
|
6
|
|
|
|
|
28
|
|
1157
|
6
|
|
|
|
|
83
|
select STDOUT; $| = 1; |
|
6
|
|
|
|
|
84
|
|
1158
|
6
|
|
|
|
|
109
|
&$code($self, $triggerval); |
1159
|
0
|
|
|
|
|
0
|
shutdown($sock, 2); |
1160
|
0
|
|
|
|
|
0
|
exit 0; |
1161
|
|
|
|
|
|
|
} else { |
1162
|
|
|
|
|
|
|
# in parent |
1163
|
25
|
50
|
|
|
|
267
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
1164
|
25
|
|
|
|
|
1231
|
$triggerval = &$despatcher; |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
} |
1167
|
2
|
|
|
|
|
2178791
|
wait; # for last child |
1168
|
2
|
|
|
|
|
189
|
return; |
1169
|
|
|
|
|
|
|
} |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
sub run { |
1172
|
14
|
|
|
14
|
0
|
79
|
my $self = shift; |
1173
|
14
|
50
|
|
|
|
157
|
$NetServer::Debug && print STDERR "run() ...\n"; |
1174
|
14
|
100
|
66
|
|
|
173
|
if ( (! defined ($self->mode())) || (lc($self->mode()) eq "forking")) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1175
|
1
|
|
|
|
|
16
|
$self->run_fork(); |
1176
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "select") { |
1177
|
0
|
|
|
|
|
0
|
$self->run_select(); |
1178
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "select_fast") { |
1179
|
0
|
|
|
|
|
0
|
$self->run_select_fast(); |
1180
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "client") { |
1181
|
11
|
|
|
|
|
232
|
$self->run_client(); |
1182
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "threaded") { |
1183
|
0
|
|
|
|
|
0
|
$self->run_thread(); |
1184
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "prefork") { |
1185
|
2
|
|
|
|
|
16
|
$self->run_prefork(); |
1186
|
|
|
|
|
|
|
} else { |
1187
|
0
|
|
|
|
|
0
|
my $aargh = "Unknown mode: " . $self->mode() . "\n"; |
1188
|
0
|
|
|
|
|
0
|
die $aargh; |
1189
|
|
|
|
|
|
|
} |
1190
|
2
|
|
|
|
|
67
|
return; |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
sub ok_to_serve($$) { |
1194
|
|
|
|
|
|
|
# internal sub. Given a ref to a Server object, and an IO::Socket::INET, |
1195
|
|
|
|
|
|
|
# see if we are allowed to serve the request. Return 1 if it's okay, 0 |
1196
|
|
|
|
|
|
|
# otherwise. |
1197
|
6
|
|
|
6
|
0
|
41
|
my ($self, $new_sock) = @_; |
1198
|
6
|
|
|
|
|
280
|
my ($junk, $peerp) = unpack_sockaddr_in($new_sock->peername()); |
1199
|
6
|
|
|
|
|
1853
|
my ($peername) = gethostbyaddr($peerp, AF_INET); |
1200
|
6
|
|
|
|
|
141
|
my ($peeraddr) = join(".", unpack("C4", $new_sock->peeraddr())); |
1201
|
6
|
|
|
|
|
810
|
$self->peer([ $peername, $peeraddr]); |
1202
|
|
|
|
|
|
|
$NetServer::Debug && |
1203
|
6
|
50
|
|
|
|
39
|
print STDERR "$0:$$: request from ", join(" ", @{$self->peer()}), "\n"; |
|
0
|
|
|
|
|
0
|
|
1204
|
6
|
50
|
33
|
|
|
90
|
return 1 if ((! defined($self->forbidden())) && |
1205
|
|
|
|
|
|
|
(! defined($self->allowed()))); |
1206
|
|
|
|
|
|
|
# if we got here, forbidden or allowed are not undef, |
1207
|
|
|
|
|
|
|
# so we have to do some checking |
1208
|
|
|
|
|
|
|
# Now we have the originator's hostname and IP address, we check |
1209
|
|
|
|
|
|
|
# them against the allowed list and the forbidden list. |
1210
|
0
|
|
|
|
|
0
|
my ($found_allowed, $found_banned) = 0; |
1211
|
0
|
0
|
|
|
|
0
|
if(defined ($self->allowed())) { |
1212
|
|
|
|
|
|
|
ALLOWED: |
1213
|
0
|
|
|
|
|
0
|
foreach (@{ $self->allowed() }) { |
|
0
|
|
|
|
|
0
|
|
1214
|
0
|
0
|
|
|
|
0
|
next if (! defined($_)); |
1215
|
0
|
0
|
0
|
|
|
0
|
if (($peername =~ /^$_$/i) || ($peeraddr =~ /^$_$/i)) { |
1216
|
0
|
|
|
|
|
0
|
$found_allowed++; |
1217
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
1218
|
|
|
|
|
|
|
print STDERR "allowed: $_ matched $peername or $peeraddr\n"; |
1219
|
0
|
|
|
|
|
0
|
last ALLOWED; |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
} |
1222
|
|
|
|
|
|
|
} |
1223
|
0
|
0
|
|
|
|
0
|
if(defined ($self->forbidden())) { |
1224
|
|
|
|
|
|
|
FORBIDDEN: |
1225
|
0
|
|
|
|
|
0
|
foreach (@{ $self->forbidden() } ) { |
|
0
|
|
|
|
|
0
|
|
1226
|
0
|
0
|
|
|
|
0
|
next if (! defined($_)); |
1227
|
0
|
0
|
0
|
|
|
0
|
if (($peername =~ /^$_$/i) || ($peeraddr =~ /^$_$/i)) { |
1228
|
0
|
|
|
|
|
0
|
$found_banned++; |
1229
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
1230
|
|
|
|
|
|
|
print STDERR "forbidden: $_ matched $peername ", |
1231
|
|
|
|
|
|
|
"or $peeraddr\n"; |
1232
|
0
|
|
|
|
|
0
|
last FORBIDDEN; |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
} |
1235
|
|
|
|
|
|
|
} |
1236
|
0
|
0
|
0
|
|
|
0
|
($found_banned && ! $found_allowed) && return 0; |
1237
|
0
|
0
|
0
|
|
|
0
|
($found_allowed && ! $found_banned) && return 1; |
1238
|
0
|
0
|
0
|
|
|
0
|
($found_allowed && $found_banned) && return 0; |
1239
|
0
|
|
|
|
|
0
|
return 0; |
1240
|
|
|
|
|
|
|
} |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
#sub _new_fifo { |
1243
|
|
|
|
|
|
|
# my $self = shift; |
1244
|
|
|
|
|
|
|
# # create a new named pipe. Return its filename. This is used by |
1245
|
|
|
|
|
|
|
# # the preforked server for children to send information back to their |
1246
|
|
|
|
|
|
|
# # parent. |
1247
|
|
|
|
|
|
|
# my $fname = "/tmp/fifo.$$"; |
1248
|
|
|
|
|
|
|
# my $mode = 666; |
1249
|
|
|
|
|
|
|
# umask(0777); # possible security hole |
1250
|
|
|
|
|
|
|
# mkfifo($fname, $mode) or die "Unable to mkfifo(): $!\n"; |
1251
|
|
|
|
|
|
|
# return $fname; |
1252
|
|
|
|
|
|
|
#} |
1253
|
|
|
|
|
|
|
# |
1254
|
|
|
|
|
|
|
#sub _read_fifo { # Blocking read |
1255
|
|
|
|
|
|
|
# my $self = shift; |
1256
|
|
|
|
|
|
|
# # read a line from the designated fifo named $self->fifo() |
1257
|
|
|
|
|
|
|
# my $handle = $self->fifo(); |
1258
|
|
|
|
|
|
|
# $SIG{ALRM} = sub { close FIFO }; |
1259
|
|
|
|
|
|
|
# open(FIFO, "<$handle") or die "Can't open $handle: $!\n"; |
1260
|
|
|
|
|
|
|
# alarm(1); |
1261
|
|
|
|
|
|
|
# my @buffer = (); |
1262
|
|
|
|
|
|
|
# alarm(0); |
1263
|
|
|
|
|
|
|
# close FIFO; |
1264
|
|
|
|
|
|
|
# return @buffer; |
1265
|
|
|
|
|
|
|
#} |
1266
|
|
|
|
|
|
|
# |
1267
|
|
|
|
|
|
|
#sub _write_fifo { # Non-blocking write |
1268
|
|
|
|
|
|
|
# my $self = shift; |
1269
|
|
|
|
|
|
|
# my @args = @_; |
1270
|
|
|
|
|
|
|
# my $handle = $self->fifo(); |
1271
|
|
|
|
|
|
|
# $SIG{ALRM} = sub { close FIFO }; |
1272
|
|
|
|
|
|
|
# open(FIFO, "+>$handle") or die "Can't open $handle: $!\n"; |
1273
|
|
|
|
|
|
|
# alarm(1); |
1274
|
|
|
|
|
|
|
# print FIFO @_; |
1275
|
|
|
|
|
|
|
# alarm(0); |
1276
|
|
|
|
|
|
|
# close FIFO; |
1277
|
|
|
|
|
|
|
# return; |
1278
|
|
|
|
|
|
|
#} |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
sub quit { |
1281
|
2
|
|
|
2
|
1
|
15
|
my ($self) = shift; |
1282
|
2
|
50
|
|
|
|
22
|
$NetServer::Debug && print STDERR "called shutdown(): root_pid is ", |
1283
|
|
|
|
|
|
|
$self->root_pid(), "\n"; |
1284
|
2
|
|
|
|
|
37
|
kill 15, $self->root_pid(); |
1285
|
2
|
|
|
|
|
1366
|
exit; |
1286
|
|
|
|
|
|
|
} |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
sub AUTOLOAD { |
1289
|
278
|
|
|
278
|
|
898
|
my ($self) = shift; |
1290
|
278
|
|
|
|
|
842
|
my ($name) = $NetServer::Generic::AUTOLOAD; |
1291
|
278
|
|
|
|
|
4069
|
$name =~ s/.*://; |
1292
|
278
|
100
|
|
|
|
1276
|
if (@_) { |
1293
|
108
|
|
|
|
|
999
|
my ($val) = shift; |
1294
|
|
|
|
|
|
|
# rudimentary type checking |
1295
|
108
|
|
100
|
|
|
1756
|
my ($r) = (ref($val) || "scalar"); |
1296
|
108
|
50
|
|
|
|
1107
|
if (! exists ($self->{tags}->{$name})) { |
1297
|
0
|
|
|
|
|
0
|
warn "\tno such method: $name\n"; |
1298
|
0
|
|
|
|
|
0
|
return undef; |
1299
|
|
|
|
|
|
|
} |
1300
|
108
|
50
|
|
|
|
3243
|
if ($r !~ /$self->{tags}->{$name}/i) { |
1301
|
0
|
|
|
|
|
0
|
warn "\t", ref($val), ": expecting a ", $self->{tags}->{$name}, "\n", "\tgot [", join("][", @_), "]\n"; |
1302
|
0
|
|
|
|
|
0
|
return undef; |
1303
|
|
|
|
|
|
|
} |
1304
|
108
|
|
|
|
|
1059
|
return $self->{$name} = $val; |
1305
|
|
|
|
|
|
|
} else { |
1306
|
170
|
|
|
|
|
5523
|
return $self->{$name}; |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
1; |
1312
|
|
|
|
|
|
|
|