Net-Server-2.008/ 0000755 0001750 0001750 00000000000 12334210320 012167 5 ustar paul paul Net-Server-2.008/Changes 0000644 0001750 0001750 00000070705 12334210017 013476 0 ustar paul paul Revision history for Perl extension Net::Server.
2.008 May 12 2014
- Long awaited patch for the IO::Socket::SSL default verify mode bug
- Add Time::HiRes as a dep
- Update to new github hosted repository with all legacy code imported
- Beginnings of change to dzil based releases
- There are several dozen outstanding bugs that will be addressed in the next release
2.007 Jan 09 2013
- Update run_dequeue to clear out signals after fork.
- Add serialize none for PreFork and PreForkSimple servers that at not multi-port.
- Allow for auto-assigned port to be the same when hostname resolves to multiple ips/ipvs (RT #78024)
- Fix bug in header parsing (RT #82125)
- Fix warning bug (RT #78828)
- Fix wrong wantarray context in Proto::TCP (RT #31437)
- Simplify _get_commandline to just use $0 - no fishing in proc (RT #80260)
- Fix uninitialized begin (RT #78830)
- Fix misplaced +2 (RT #78827)
- Fix various spelling errors and pod nits (RT #77994)
- Allow for log calls from the MUX object (RT #78514)
- Give an error at begin time of SSLEAY if the need Fcntl function is not available on this platform (RT #82542)
- Make all of the tests self cancel much earlier if there is an error handling the request.
- Add a stronger timeout to PreFork tests - ideally we'd like to find a real way to test these one windows (RT #811411, #81993) - still broken - but shouldn't hang
- Superficial changes to help packagers not list Log4perl as dependency (it is optional)
2.006 Jun 20 2012
- Allow for case where host is *, and IO::Socket::INET6 is installed, but IPv6 doesn't really work.
- Add missing child_init_hook in Fork server (so Fork can be more parallel with PreFork in some respects)
- Change BOUND_SOCKETS passing to use ; as a separator rather than a \n
2.005 Jun 12 2012
NOTE: ipv now defaults to *
- Change the default of ipv from 4 to *. This means if a host of * (default), or a named host is used, any available IPv4 OR IPv6 address will be used.
- Allow for explicit close_client_stdout call
- Add dispatch methods and app setup to HTTP
- Allow for exec_fork_hook in HTTP
- Make sure errors in HTTP use correct logging mechanisms (and do not die un-needed)
- Fix 500 call in PSGI
- Fix send_header
2.004 Jun 08 2012
NOTE: Version 2.005 will change the default ipv value to * meaning it will attempt to bind IPv4 and IPv6 if they are available if you pass a hostname
- Add Net::Server::Proto->get_addr_info which can more reliably resolve domain information.
- Use that information to figure out an appropriate host to bind to for tests
- Make get_addr_info less reliant on magic values of sysctl net.ipv6.bindv6only
- Allow all tests to function in IPv6 only environments
- Fix broken number of tests in test suite
- Add warnings about changes to the default value of ipv coming in 2.005
2.003 Jun 06 2012
- Make the logging system pluggable
- Added net-server server executor for writing easier one line servers
- Sys::Syslog and Log::Log4perl are now moved out to their own module subsystems
- Added full apache style HTTP log formatting for the HTTP server
- Allow for ipv to be specified as part of host, or proto, or passed via $ENV{'IPV'}
- Add apache style access logging (access_log_file and access_log_format) to HTTP
- Allow HTTP header parsing to not untaint the headers (thanks Miko O'Sullivan)
- Fix missing legacy NS_unix_path call (missing since 2.000)
- Fix a bug in MultiType that prevented calling server_type HTTP
2.002 May 31 2012
- Make HTTP output header parsing more consistent - and catch more errors
- Add exec_cgi and exec_trusted_perl methods to HTTP
- Add bugfix for ipv=>"*" combined with UNIX sockets. (Mark Martinec)
- Fix the SSL_test.t to use exit rather than quit so the parent departs
2.001 May 30 2012
- Bug fix wrong usage of File::Temp::tempfile.
- Fix HTTP_COOKIES to be HTTP_COOKIE
- Handle multiple header values better in HTTP
- Add Log::Log4perl logging courtesy of TONVOON@cpan
2.000 May 30 2012
- Sorry for the amazingly long delay. This release represents change to much of the code base. Future patch submissions should be more promptly handled
- Bring Net::Server::Proto::SSL back. It is now fully functional under all scenarios, including IPv4 and IPv6
- Change Proto interface to allow passing more information. This represents an internal API change.
- Updates to the HUP mechanisms to make sure we rebind all types of ports correctly.
- Add IPv6 integration via ::1 style addresses as well as the ipv configuration parameter (Mark Martinec)
- Added graceful shutdown (Tatsuhiko Miyagawa)
- Added hot deploy via TTIN and TTOU (Tatsuhiko Miyagawa)
- Internal code retidying
- Finish out support for connecting to ports by service name
- Don't loose track of fork and prefork children on a hup - make sure to actively wait them off
- Correct accept to take a classname, and optionally be called in array context
- Cleanup numerous configuration issues.
- Added sig_passthrough option to Fork, PreFork, and PreForkSimple servers allowing for arbitrary signals to propagate to children
- Add syswrite/sysread support to SSLEAY (Sergey Zasenko).
- Add PSGI module.
- Many small accumulated bugfixes.
0.99 Jul 13 2010
- Add customizable check_for_spawn and min_child_ttl settings in PreFork (Graham Barr)
- Add other_child_died_hook (Daniel Kahn Gillmor)
- Make Multiplex do $mux->add($sock) for UDP sockets (Kristoffer Møllerhøj)
- Change Net::Server::Daemonize to use kill 0 rather than the unportable `ps`
- Fix calling conventions of MultiType
- Avoid select in SSLEAY that was allowing for infinite spin loop
- Fix tie_stdout mode to not warn about unopen handles.
- Added Net::Server::HTTP base class for basic HTTP daemon handling.
- Change examples/httpd to use Net::Server::HTTP
0.98 May 05 2010
- Add SSLeay proto - finally a workable SSL solution.
- Add minimal Net::Server::TiedHandle to allow for STDIN and STDOUT to work with SSLEAY
- Net::Server::TiedHandle also support tied_stdin_callback and tied_stdout_callback
Feb 08 2008
- Allow for port => 0 which lets the OS auto assign a port on some OSes (Blackie Hlasek)
- Add idle_loop_hook to PreForkSimple and PreFork (David Zuhn)
- Add consistent formatting capabilities to the log method (whethere Syslog is used or not) (David Zuhn)
- Warn when default listen value is used - try to make it a sensible default (Mark Martinec)
- Allow for non-zero exit value - particularly when called from fatal (David Schweikert)
0.97 Jul 25 2007
- Allow for better handling of setlogsock depending upon the version of Sys::Syslog installed (David Schweikert)
- Update examples with minimal pod and working synopses
- Added post_client_connection_hook (Mihail Nasedkin)
0.96 Mar 23 2007
- Allow for conf_file to be specified in the default_values.
- Add perldoc for why we use a template in options.
- Fix syslog log options regex again (Carlos Velasco)
- Fix ->autoflush (needs FileHandle) (Paul Miller)
- Add handle_syslog_error to allow catching errors during syslog writes (Patrik Wallstrom)
- Add open_syslog to slightly abstract opening of syslog.
- Add numerous patches from Rob Mueller to cleanup child accounting in PreFork server.
0.95 Feb 02 2007
- Warn clean on the chld hanlder in PreFork. (Michael Virnstein)
- Allow lock_file for lock serialization to only be opened once (Rob Mueller)
- Add additional log messages during failure in accept (Mark Martinec)
- Fix double decrement bug in PreFork.pm (Bill Nesbitt, Carlos Velasco) (rt #21271)
- Fix precedence bug with non-parened open (John W. Krahn)
- Check setuid better after POSIX setuid (Ricardo Signes) (rt #21262)
- Update Syslog options parsing (Carlos Velasco) (rt #21265)
- Allow no_client_stdout to work with Multiplex (Steven Lembark)
- Allow Sys::SysLog keyworks be passed through the ->log method (Peter Beckman)
- Allow more characters through in syslog_ident (Peter Beckman)
- Fix Fork server bug which had post_accept_hook called twice (Curtis Wilbar)
- Added pre_fork_hook to Fork server to handle removed duplicate post_accept_hook call.
- Reopen STDIN/STDOUT to /dev/null at end of child connection to avoid spurious warnings (Rob Mueller)
- Don't process STDIN/STDOUT in post_accept if udp_true (Rob Mueller)
- Cleanup child processing code in PreFork server (Rob Mueller)
- Try and let tests fail gracefully if localhost is not setup properly (Peter Beckman)
- Add numerous tests for configuration passing.
- Add perldoc about adding your own custom options.
0.94 Jul 08 2006
- Add nofatal to Sys::Syslog::openlog if Sys::Syslog
version >= 0.15 (thanks to DSCHWEI on cpan)
- Added the leave_children_open_on_hup flag which leaves
open connections open when the server
is hupped. It is false by default.
- Make sure new and run can both take a hash or a hashref of values.
- More fixes to HUP under taint (thanks to LUPE on cpan)
- Allow for port, host, and proto to be passed as arrayrefs to run and new.
- Fix bug in a check for dead child processes algorithm in
PreFork server (thanks to Michael Virnstein).
0.93 Mar 23 2006
- Allow for get sock info routines to use $peer->{client} rather than STDIN
which may not be set if the "no_client_stdout" flag is set. (thanks to
Mark Martinec for pointing this out)
0.92 Mar 13 2006
- Allow for duplicated STDIN and STDOUT to properly close.
Previously they closed because they were simple symbol globs.
Now they need an explicit close be cause they are opened to
the client socket's file descriptors.
- Add flag to disable all of the binding of client to STDIN and STDOUT
0.91 Mar 08 2006
- Abstract shutdown_sockets method that is called at the end
of server_close (to allow for calling in other places).
- Make sure close_children unsets the overridden signals in
the forked and preforked servers.
- Better handling of STDIN and STDOUT as provided by tye on
perlmonks in response to Ben Cohen's question (in node
http://www.perlmonks.org/?node_id=534791)
- Finally added a new method.
- Added much missing perldoc.
- Pass parameters to ->run the second time it is called. This
allows for multitype to handle more parameters - but needs
to be tested for all use cases (it could result in array fields
getting multiple entries which should be fine in most cases).
Thanks to Aron Ujvari for pointing this out.
- Add default_values method (suggested by Malte S. Stretz).
- Fix udp_broadcast issue (fix by Rob Mueller)
0.90 Dec 05 2005
- Make HUP work correctly on Multiplex server.
- Allow socket files to cleanup correctly.
- Allow Net::Server::Daemonize to function properly in Taint
mode again (broken in .88).
- Add ->commandline method to allow for getting and setting
the commandline for use during a HUP. This is to allow for
untainting as necessary.
- Add ->can_read_hook (see the documentation) to allow for
processing of arbitrary handles in accept_multi_port.
0.89 Nov 22 2005
- Added SSL_passwd_cb to Proto/SSL.pm (Irving A. Bermudez S.)
- Fix rt #13450 which is caused by broken POSIX::setuid on
perl 5.8.0 on RedHat 9.0.
- Allow for graceful skipping if a port is configured twice in
the configuration file.
- Allow tests that can pass to pass on Win32 (skip those that cannot)
- Allow "-" in user names. (Carl Lewis)
- Add Reuse = 1 to Proto::UDP. (Slaven Rezic)
- Allow for udp_broadcast setting in Proto::UDP. (Tim Watt)
- Add bug note to Proto::SSL (Christopher A Bongaarts)
- setsid property is now boolean rather than checking definedness.
- Command line parameters override conf file parameters.
- Store command line a little better in preparation for HUP.
- Allow for cleaner HUP and better error if process already running.
0.88 Jun 21 2005
- Change maintainer back to paul@seamons.com (Paul Seamons)
- Add run_n_children_hook to prefork servers (At suggestion of
James Fitzgibbon and Paul B. Henson)
- Make delete child only delete children it knows about.
Fixes ancient bug http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=168784
filed by Christian Mock and worked on by Lucas Filipozzi.
- Store $ENV{PWD} as part of script name for HUP'ing (Russel Pettway)
- Allow PreFork and PreForkSimple to have child mark
explicitly as done the same as other server types via
the ->done(1) call. (Idea from Marc Martinec)
- After numerous requests, the CHLD SIGNAL is now
set to DEFAULT in the child process of PreFork and
PreForkSimple servers. This should allow grand child processes to
run without affecting the child process (or parent).
- Fix parent/child communication channel buffering issue (Matt Sergeant)
- Check for child's sock before closing with
child_communication enabled (Alexander Hlawenka)
- Documentation fix (Mark Morgan)
- Allow 'stream' option for syslog_logsock property (Daniel Matuschek)
- Fix syslog format vulnerability. (Carlos Velasco) This has
potential to break some log implementations that were
planning on ->log passing all of @_ to syslog. Now only the
first item from @_ is passed as the message.
- Allow for '-' in group names. (Corey Minyard)
- Prevent locking caused by interupt of flock (Dietmar Maurer [cpan #11693])
- Finally fix UID/GID bugs during daemonization. This is the
biggest bug winner. The new model Calls POSIX::setuid and
setgid and tests for success by checking the values of $<
and $( and not by checking the response of the setuid/setgid functions.
- Add CIDR style lookups for allow/deny using cidr_allow and
cidr_deny (Carsten Wolff)
- Allow for port configured in perl to not have to be arrayref.
0.87 Feb 14 2004
- Patch by Enrik.Berkhan@planb.de (Enrik Berkhan)
that fixes RT Bug #3671
- Patch by chris@dyndns.org (Chris Reinhardt)
Integrate pre_accept_hook and post_accept_hook
into Net::Server::Fork
0.86 Nov 06 2003
- Changed maintainer to bbb@cpan.org (Rob Brown).
- Patch to fix Net::Server::Daemonize setuid bug:
http://www.ijs.si/software/amavisd/net-server.patch
- Add a fix in the argument handling of configure
to account for some alpha systems (James Vasak)
- For RedHat 8.0 perl.req, avoid demanding that
perl(IO::Muliplex) and perl(IO::Socket::SSL)
rpms are installed just to use Net::Server.
0.85 Mar 06 18:00 2003
- Lower timeouts during tests (Anil Madhavapeddy)
- Add configure_hook to MultiType (Michael Alan Dorman)
- More graceful exit of children in PreForkSimple (Helge Kraenz)
- Correct test for POSIX::setuid(0) success (Peter Chen)
- Allow DOS filenames for conf files (Mark M. Adkins)
- Allow for ndelay on Sys::Syslog::openlog (Doug Perham)
- Add documentation about run_dequeue.
- Add run_dequeue feature to Multiplex personality.
0.84 May 22 08:00 2002
- Safer peername check in get_client_info to
avoid crashing under certain conditions.
- Create noarch RPM since Net::Server is pure perl.
- Always chown log and pid files when started as root
but running as non-root.
- More graceful exit of children in PreFork
- Kill children with a kill 15 rather than kill 2 -
Fixes on Tru64 UNIX (Marco Sbodio)
- Allow for SOCK_STREAM and SOCK_DGRAM to be passed
as strings to proto (ie "/tmp/path/|SOCK_STREAM|unix") (Andrzej Filip)
- Backward compatibility fix for IO::Socket usage (Matt Sergeant)
- Avoid reopening STDIN and STDOUT in INET mode. (Bernard Quatermass)
0.83 Mar 26 15:33 2002
- Prevent race condition warning between accept on socket
and assigning client to STDIN
- Fix bug in Net::Server::Proto::UNIX which affected
older perls (<= 5.005)
- Allow failed attempt to change user to continue with
warning if not root.
- Add parent/child communication code to PreFork.pm based off
code submitted by Vadim. Allows children to speak to parent.
- Improved accounting of child processes in PreFork.
- Add spec file for rpm.
0.82 Jan 29 16:20 2002
- Add changes pointed out by Vadim to make sure
that SSL client handle is blessed into correct class.
0.81 Nov 19 12:39 2001
- Fix Net::Server::Fork - Bug in forking server
once parent has reached max_servers caused.
slow infinite loop and no processing of connections.
- Some perldoc cleanups
- Don't require IO::Multiplex for base test.
0.80 Nov 14 09:30 2001
- Fix Net::Server::Multiplex::MUX::mux_eof
to pass a ref to the remaining data.
0.79 Oct 23 12:00 2001
- Added Net::Server::Multiplex
- NOTE: IO::Multiplex >= 1.01 is required
to use this personality.
0.78 Sep 28 9:13
- Added post_child_cleanup_hook to server_close
- Moved pre_server_close_hook inside server_close
- Various small cleanups
- Added no_close_by_child flag (see perldoc)
0.77 Aug 27 10:00
- Added dequeuing ability to Fork mode server.
- All Fork and PreFork modes now have dequeue ability.
0.76 Aug 24 11:16
- Added Net::Server::PreForkSimple
- Simpler PreFork server that only attempts to
maintain a constant number of child servers.
- Changed Net::Server::PreFork to subclass
off of Net::Server::PreForkSimple. No
functional changes.
- Fixed a bug in Net::Server::Daemonize::set_user.
- Fixed syntax bug on 5.005_03 in Proto's
0.75 Aug 23 10:49
- Both Net::Server::Fork and Net::Server::PreFork are
using safe signals via Net::Server::SIG.
- Net::Server::PreFork has new child managment model.
NOTE: spare_servers is no longer used. It
has been replaced by min_spare_servers and
max_spare_servers. This is a major change.
The server will die if these parameters are
not properly set.
- operates better under high loads
- provides better clean up of waiting servers.
- more configurable.
- Read the perldoc for updates.
- Net::Server::Fork and Net::Server::PreFork HUP
properly again.
- t/Server_PreFork.t and t/Server_Fork.t execute
properly.
- Fix in Multiport accept with signals.
- Updated perldocs
0.73 Aug 21 17:06
- Net::Server::PreFork is on safe signals.
0.72 Aug 21 16:22 2001
- Beginning work on Safe signals
- Net::Server::Fork is on safe signals.
- Added Net::Server::SIG
- Added examples/sigtest.pl
0.71 Aug 17 15:51 2001
- Die on failed change to another user or group.
WARNING: No longer defaults to nobody.nobody.
Defaults to currently running user and group.
- Various cleanups with file removal.
- All files to be removed are now chowned.
0.70 Aug 17 10:34 2001
- Added support for different protocols to Net::Server.
This implemented via Net::Server::Proto and its classes.
Included Net::Server::Proto::TCP,
Net::Server::Proto::UDP,
Net::Server::Proto::UNIX,
and experimental Net::Server::Proto::SSL.
TCP, UDP, and UNIX are fully tested.
- Added Net::Server::Daemonize.
- Allows for modular daemonization/forking routines.
- Allowed for configure to be called multiple times.
Configure method can be called at later times during
server startup. Arguments are cached. This allows
new protocols to add arguments without modification
to Net::Server base class.
- Updated perldocs.
No more protocol specific information in central perldoc.
More information on new protocol layout.
- Added t/UNIX_test.t
- Added examples/connection_test.pl
- UNIX, UDP, and TCP types are fully operational.
Server can bind to all three types.
Properties are determined according to type.
Server can HUP on all three types.
- SSL type added, but experimental (read "extremely alpha")
0.65 Jul 05 22:01 2001
- Modified test suite to no longer depend upon
hard coded ports. Improves test reliability
on systems where reuse on a socket is not
reliable itself.
0.64 Jul 03 21:21 2001
- Allow fall back to main run method at server close
- Clean up signal processing in PreFork server
- Clean up child management in PreFork server
- Added run_dequeue and dequeue methods to the
PreFork server (intended to allow for
management of items such as mail queues)
0.63 May 07 22:39 2001
- Updated UDP parameter names. Names are now
udp_recv_len (previously udp_packet_size)
and udp_recv_flags (previously udp_packet_offset).
- Updated udp_server.pl to use new names.
0.62 May 01 00:44 2001
- Updated to use getsockopt (determine proto on the fly)
- Updated perldoc.
- Added udp_server.pl example.
- Added UDP_test.t for "make test".
- Allow customization of udp recv parameters.
0.61 Apr 30 06:32 2001
- Sig HUP with UDP now works.
- Peer info is correctly accessed under UDP
- Net::Server::INET will not allow one server
to do both tcp and udp without special
parameters being passed to the server.
- Need to make test program for UDP.
0.60 Apr 28 01:56 2001
- Added support for UDP. Can now simultaneously accept
TCP and UDP.
Still to do:
- allow for SIG HUP under UDP
- better determination of peerinfo under UDP
- clean up inetd mode.
- Added restart_close_hook.
- Added restart_open_hook.
- Added more documentation (socket access, restarting,
protocols)
0.59 Apr 24 07:40 2001
- Forced STDIN,STDOUT,and STDERR to reopen to /dev/null
if setsid or log_file is set.
This allows for true daemonization (so
no output ends up at the terminal).
- Made appropriate changes in MultiType as well.
0.58 Apr 06 12:29 2001
- SIG HUP is complete. Fixed bug in SIG HUP'ing
PreFork mode. Now effectively restarts.
- Various clean ups in code.
- More unification of code.
0.57 Mar 29 01:36 2001
- SIG HUP is now functional on multiport mode under
Single and Fork Mode. No functionality is
lost under PreFork, but HUP'ing results in
seg fault.
- Various bug fixes.
0.56 Mar 20 12:34 2001
- Catch SIG pipes
- Clean up of existing signal handling.
- Trim memory in PreFork
0.55 Mar 19 10:44 2001
- Allow overwrite of pid file - safe as other user.
- More unified Signal handling, removal of duplicate code.
- Allow Fork Server to shutdown the socket immediately
after client accept to allow parent to HUP more
easily.
- Check to see if parent process is still around in PreFork
Server. (Don't keep running if parent was
"kill 9"ed.)
- Save commandline parameters in preparation for HUP
0.54 Mar 16 12:47 2001
- Better handling of sigs in prefork
- Improved logic on child coordination routine
- Added parent_read_hook
- Added httpd example
- Added LoadTester.pl example
0.53 Mar 14 01:13 2001
- Allow host to be set to '*'
Allows for the server to bind to port whatever
on all addresses at that box.
- Make passing of host and proto on command line
taint clean.
- Added setsid functionality.
- Added syslog_facility option - default is daemon.
- Changed Fork and PreFork to handle $SIG{CHLD} in
a more reliable fashion.
- Added parent_read_hook
0.52 Mar 13 01:16 2001
- Added syslog ability.
This allows for logging to syslog instead
of STDERR or a log file. Logging still
takes place via $self->log() but is configurable
at startup.
- Standardized existing log numbers to match syslog levels.
0.51 Mar 10 16:35 2001
- Added piped serialization option.
This allows for serialization to be done on
a wider range of machines. Flock is more
bulletproof, but pipe is more portable.
See the Net::Server::PreFork manpage.
0.50 Mar 10 10:06 2001
- Added serialize option to PreFork.
This allows for serialization to be turned on
even on non_multi port process - this is
done to get around some OS's which don't
allow children to accept on the same socket
at the same time.
- Added semaphore type to the serialize option
This type uses IPC::Semaphore instead of flock
to serialize the child accept sequence.
Thanks to Bennett Todd for sample code.
0.48 Mar 08 23:57 2001
- Catch $SIG{INT}, $SIG{TERM} and $SIG{QUIT} in PreFork and Fork.
This allows parent to shutdown children properly.
- Catch $SIG{HUP} (currently shuts down server, needs to
be able to restart server re-reading conf file)
- Changed pid_file creation to after chroot, change of group
and change of user - making sure the server has
permission to write out the pid file.
- Remove use of "use subs" in PreFork.
0.47 Mar 08 07:03 2001
- Fix reverse lookup bug - thanks to Jonathan J. Miner for
pointing out the missing pieces.
- Cleaned up pod examples
- Clarified some of the pod
0.46 Mar 05 07:37 2001
- secure removal of pid_file - only happens on success -
possibly should only happen after process has become
another user
- secure removal of lock_file - only happens if we generated it
- added child_init_hook and child_finish_hook to PreFork
- changed pre_configure_hook to configure_hook
- added simple httpd example script
0.45 Mar 02 00:44 2001
- clean up make process.
- change version to hard coded number.
- improve testing scripts
0.44 Mar 01 00:55 2001
- partitioned properties in single hashref value.
- changed versioning system to use cvs revision.
- general clean up and add documentation.
0.43 Feb 28 01:08 2001
- this revision and last add bulk of documentation.
-various clean ups
0.4.1 Feb 26 17:48 2001
- first build. Up to this point many revisions, bug fixes
and optimizations had been made.
0.1.0 Feb 08 06:28 2001
- first cvs check in. Up to this point, much thought and
research had gone into the server.
Net-Server-2.008/bin/ 0000755 0001750 0001750 00000000000 12334210320 012737 5 ustar paul paul Net-Server-2.008/bin/net-server 0000755 0001750 0001750 00000006541 12331755703 015005 0 ustar paul paul #!/usr/bin/env perl
package net_server;
use strict;
use warnings;
if (grep {$_ eq '--help' || $_ eq '-h'} @ARGV) {
require Pod::Usage;
Pod::Usage::pod2usage(-verbose => 1);
exit;
}
my $pkg;
if (@ARGV
&& $ARGV[0]
&& $ARGV[0] =~ /^(\w+)$/
&& ($pkg = $1)
&& eval { require "Net/Server/$pkg.pm" }
) {
$pkg = "Net::Server::$pkg";
} else {
if ($pkg && grep {-e "$_/Net/Server/$pkg.pm"} @INC) {
die "Error trying to become a Net::Server::$pkg:\n\n$@";
}
$pkg = 'Net::Server::MultiType';
}
require base;
import base $pkg;
__PACKAGE__->run;
exit;
sub default_port {
my $self = shift;
return 8080 if $> && $self->isa('Net::Server::HTTP');
return $self->SUPER::default_port;
}
__END__
=head1 NAME
net-server - Base Net::Server starting module
=head1 SYNOPSIS
net-server [base type] [net server arguments]
net-server PreFork ipv '*'
net-server HTTP
net-server HTTP app foo.cgi
net-server HTTP app foo.cgi app /=bar.cgi
net-server HTTP port 8080 port 8443/ssl ipv '*' server_type PreFork --SSL_key_file=my.key --SSL_cert_file=my.crt access_log_file STDERR
=head1 DESCRIPTION
The net-server program gives a simple way to test out code and try
port connection parameters. Though the running server can be robust
enough for full tim use, it is anticipated that this binary will just
be used for basic testing of net-server ports, acting as a simple echo
server, or for running development scripts as CGI.
=head1 OPTIONS
=over 4
=item C
The very first argument may be a Net::Server flavor. This is given as
shorthand for writing out server_type "ServerFlavor". Additionally,
this allows types such as HTTP and PSGI, which are not true
Net::Server base types, to subclass other server types via an
additional server_type argument.
net-server PreFork
net-server HTTP # becomes a HTTP server in the Fork flavor
net-server HTTP server_type PreFork # preforking HTTP server
=item C
Port to bind upon. Default is 80 if running a HTTP server as root,
8080 if running a HTTP server as non-root, or 20203 otherwise.
Multiple value can be given for binding to multiple ports. All of the
methods for specifying port attributes enumerated in L
and L are available here.
net-server port 20201
net-server port 20202
net-server port 20203/IPv6
=item C
Host to bind to. Default is *. Will bind to an IPv4 socket if an
IPv4 address is given. Will bind to an IPv6 socket if an IPv6 address
is given (requires installation of IO::Socket::INET6).
If a hostname is given and C is still set to 4, an IPv4 socket
will be created. If a hostname is given and C is set to 6, an
IPv6 socket will be created. If a hostname is given and C is set
to * (default), a lookup will be performed and any available IPv4 or
IPv6 addresses will be bound. The C parameter can be set
directly, or passed along in the port, or additionally can be passed
as part of the hostname.
net-server host localhost
net-server host localhost/IPv4
=back
There are many more options available. Please see the L
documentation.
=head1 AUTHOR
Paul Seamons
=head1 LICENSE
This package may be distributed under the terms of either the
GNU General Public License
or the
Perl Artistic License
=cut
Net-Server-2.008/META.yml 0000664 0001750 0001750 00000001070 12334210320 013440 0 ustar paul paul ---
abstract: 'Extensible, general Perl server engine'
author:
- 'Paul Seamons and Rob Brown '
build_requires:
ExtUtils::MakeMaker: 0
configure_requires:
ExtUtils::MakeMaker: 0
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.133380'
license: unknown
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: 1.4
name: Net-Server
no_index:
directory:
- t
- inc
requires:
IO::Socket: 0
POSIX: 0
Socket: 0
Time::HiRes: 0
version: 2.008
Net-Server-2.008/lib/ 0000755 0001750 0001750 00000000000 12334210320 012735 5 ustar paul paul Net-Server-2.008/lib/Net/ 0000755 0001750 0001750 00000000000 12334210320 013463 5 ustar paul paul Net-Server-2.008/lib/Net/Server.pod 0000644 0001750 0001750 00000177370 12331756053 015473 0 ustar paul paul =head1 NAME
Net::Server - Extensible, general Perl server engine
=head1 SYNOPSIS
#!/usr/bin/perl -w -T
package MyPackage;
use base qw(Net::Server);
sub process_request {
my $self = shift;
while () {
s/[\r\n]+$//;
print "You said '$_'\015\012"; # basic echo
last if /quit/i;
}
}
MyPackage->run(port => 160, ipv => '*');
# one liner to get going quickly
perl -e 'use base qw(Net::Server); main->run(port => 20208)'
NOTE: beginning in Net::Server 2.005, the default value for
ipv is IPv* meaning that if no host is passed, or
a hostname is past, any available IPv4 and IPv6 sockets will be
bound. You can force IPv4 only by adding an ipv => 4
configuration in any of the half dozen ways we let you
specify it.
=head1 FEATURES
* Full IPv6 support
* Working SSL sockets and https (both with and without IO::Socket::SSL)
* Single Server Mode
* Inetd Server Mode
* Preforking Simple Mode (PreForkSimple)
* Preforking Managed Mode (PreFork)
* Forking Mode
* Multiplexing Mode using a single process
* Multi port accepts on Single, Preforking, and Forking modes
* Basic HTTP Daemon (supports IPv6, SSL, full apache style logs)
* Basic PSGI Daemon
* Simultaneous accept/recv on tcp/udp/unix, ssl/tcp, and IPv4/IPv6 sockets
* Safe signal handling in Fork/PreFork avoids perl signal trouble
* User customizable hooks
* Chroot ability after bind
* Change of user and group after bind
* Basic allow/deny access control
* Pluggable logging (Sys::Syslog, Log::Log4perl, log_file, STDERR, or your own)
* HUP able server (clean restarts via sig HUP)
* Graceful shutdowns (via sig QUIT)
* Hot deploy in Fork and PreFork modes (via sig TTIN and TTOU)
* Dequeue ability in all Fork and PreFork modes.
* Taint clean
* Written in Perl
* Protection against buffer overflow
* Clean process flow
* Extensibility
=head1 DESCRIPTION
C is an extensible, generic Perl server engine.
C attempts to be a generic server as in C
and C. It includes with it the ability to run as
an inetd process (C), a single connection server
(C or C), a forking server
(C), a preforking server which maintains a constant
number of preforked children (C), or as a
managed preforking server which maintains the number of children based
on server load (C). In all but the inetd type,
the server provides the ability to connect to one or to multiple
server ports.
The additional server types are made possible via "personalities" or
sub classes of the C. By moving the multiple types of
servers out of the main C class, the C
concept is easily extended to other types (in the near future, we
would like to add a "Thread" personality).
C borrows several concepts from the Apache Webserver.
C uses "hooks" to allow custom servers such as SMTP,
HTTP, POP3, etc. to be layered over the base C class. In
addition the C class borrows concepts of
min_start_servers, max_servers, and min_waiting servers.
C also uses the concept of an flock serialized
accept when accepting on multiple ports (PreFork can choose between
flock, IPC::Semaphore, and pipe to control serialization).
=head1 PERSONALITIES
C is built around a common class (Net::Server) and is
extended using sub classes, or C. Each personality
inherits, overrides, or enhances the base methods of the base class.
Included with the Net::Server package are several basic personalities,
each of which has their own use.
=over 4
=item Fork
Found in the module Net/Server/Fork.pm (see L).
This server binds to one or more ports and then waits for a
connection. When a client request is received, the parent forks a
child, which then handles the client and exits. This is good for
moderately hit services.
=item INET
Found in the module Net/Server/INET.pm (see L).
This server is designed to be used with inetd. The C,
C, C, and C are all overridden as these
services are taken care of by the INET daemon.
=item MultiType
Found in the module Net/Server/MultiType.pm (see
L). This server has no server functionality
of its own. It is designed for servers which need a simple way to
easily switch between different personalities. Multiple
C parameters may be given and Net::Server::MultiType will
cycle through until it finds a class that it can use.
=item Multiplex
Found in the module Net/Server/Multiplex.pm (see
L). This server binds to one or more ports.
It uses IO::Multiplex to multiplex between waiting for new connections
and waiting for input on currently established connections. This
personality is designed to run as one process without forking. The
C method is never used but the C callback
is used instead (see also L). See
examples/samplechat.pl for an example using most of the features of
Net::Server::Multiplex.
=item PreForkSimple
Found in the module Net/Server/PreFork.pm (see
L). This server binds to one or more ports and
then forks C child process. The server will make sure
that at any given time there are always C available to
receive a client request. Each of these children will process up to
C client connections. This type is good for a heavily
hit site that can dedicate max_server processes no matter what the
load. It should scale well for most applications. Multi port accept
is accomplished using either flock, IPC::Semaphore, or pipe to
serialize the children. Serialization may also be switched on for
single port in order to get around an OS that does not allow multiple
children to accept at the same time. For a further discussion of
serialization see L.
=item PreFork
Found in the module Net/Server/PreFork.pm (see
L). This server binds to one or more ports and
then forks C child process. The server will make sure
that at any given time there are at least C but not
more than C available to receive a client request,
up to C. Each of these children will process up to
C client connections. This type is good for a heavily
hit site, and should scale well for most applications. Multi port
accept is accomplished using either flock, IPC::Semaphore, or pipe to
serialize the children. Serialization may also be switched on for
single port in order to get around an OS that does not allow multiple
children to accept at the same time. For a further discussion of
serialization see L.
=item Single
All methods fall back to Net::Server. This personality is provided
only as parallelism for Net::Server::MultiType.
=item HTTP
Not a distinct personality. Provides a basic HTTP daemon. This can
be combined with the SSL or SSLEAY proto to provide an HTTPS Daemon.
See L.
=back
C was partially written to make it easy to add new
personalities. Using separate modules built upon an open architecture
allows for easy addition of new features, a separate development
process, and reduced code bloat in the core module.
=head1 SOCKET ACCESS
Once started, the Net::Server will take care of binding to port and
waiting for connections. Once a connection is received, the
Net::Server will accept on the socket and will store the result (the
client connection) in $self-E{server}-E{client}. This
property is a Socket blessed into the the IO::Socket classes. UDP
servers are slightly different in that they will perform a B
instead of an B.
To make programming easier, during the post_accept phase, STDIN and
STDOUT are opened to the client connection. This allows for programs
to be written using ESTDINE and print "out\n" to print to the
client connection. UDP will require using a -Esend call.
=head1 SAMPLE CODE
The following is a very simple server. The main functionality occurs
in the process_request method call as shown below. Notice the use of
timeouts to prevent Denial of Service while reading. (Other examples
of using C can, or will, be included with this
distribution).
#!/usr/bin/perl -w -T
package MyPackage;
use strict;
use base qw(Net::Server::PreFork); # any personality will do
MyPackage->run;
# over-ride the default echo handler
sub process_request {
my $self = shift;
eval {
local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
my $timeout = 30; # give the user 30 seconds to type some lines
my $previous_alarm = alarm($timeout);
while () {
s/\r?\n$//;
print "You said '$_'\r\n";
alarm($timeout);
}
alarm($previous_alarm);
};
if ($@ =~ /timed out/i) {
print STDOUT "Timed Out.\r\n";
return;
}
}
1;
Playing this file from the command line will invoke a Net::Server
using the PreFork personality. When building a server layer over the
Net::Server, it is important to use features such as timeouts to
prevent Denial Of Service attacks.
Net::Server comes with a built in echo server by default. You can test it
out by simply running the following from the commandline:
net-server
If you wanted to try another flavor you could try
net-server PreFork
If you wanted to try out a basic HTTP server you could use
net-server HTTP
Or if you wanted to test out a CGI you are writing you could use
net-server HTTP --app ../../mycgi.cgi
=head1 ARGUMENTS
There are at least five possible ways to pass arguments to
Net::Server. They are I, I, I, I,
I, or I.
The C method is used to determine which arguments the server
will search for and can be used to extend the parsed parameters. Any
arguments found from the command line, parameters passed to run, and
arguments found in the conf_file will be matched against the keys of
the options template. Any commandline parameters that do not match
will be left in place and can be further processed by the server in
the various hooks (by looking at @ARGV). Arguments passed to new will
automatically win over any other options (this can be used if you
would like to disallow a user passing in other arguments).
Arguments consist of key value pairs. On the commandline these pairs
follow the POSIX fashion of C<--key value> or C<--key=value>, and also
C. In the conf file the parameter passing can best be
shown by the following regular expression:
($key,$val)=~/^(\w+)\s+(\S+?)\s+$/. Passing arguments to the run
method is done as follows: C<run(key1 => 'val1')>>.
Passing arguments via a prebuilt object can best be shown in the
following code:
#!/usr/bin/perl -w -T
package MyPackage;
use strict;
use base qw(Net::Server);
my $server = MyPackage->new({
key1 => 'val1',
});
$server->run;
All five methods for passing arguments may be used at the same time.
Once an argument has been set, it is not over written if another
method passes the same argument. C will look for
arguments in the following order:
1) Arguments passed to the C method.
2) Arguments passed on command line.
3) Arguments passed to the C method.
4) Arguments passed via a conf file.
5) Arguments set in the C method.
Additionally the following hooks are available:
1) Arguments set in the configure_hook (occurs after new
but before any of the other areas are checked).
2) Arguments set and validated in the post_configure_hook
(occurs after all of the other areas are checked).
Each of these levels will override parameters of the same name
specified in subsequent levels. For example, specifying --setsid=0 on
the command line will override a value of "setsid 1" in the conf file.
Note that the configure_hook method doesn't return values to set, but
is there to allow for setting up configured values before the
configure method is called.
Key/value pairs used by the server are removed by the configuration
process so that server layers on top of C can pass and
read their own parameters.
=head1 ADDING CUSTOM ARGUMENTS
It is possible to add in your own custom parameters to those parsed by
Net::Server. The following code shows how this is done:
sub options {
my $self = shift;
my $prop = $self->{'server'};
my $template = shift;
# setup options in the parent classes
$self->SUPER::options($template);
# add a single value option
$prop->{'my_option'} ||= undef;
$template->{'my_option'} = \ $prop->{'my_option'};
# add a multi value option
$prop->{'an_arrayref_item'} ||= [];
$template->{'an_arrayref_item'} = $prop->{'an_arrayref_item'};
}
Overriding the C method allows for adding your own custom
fields. A template hashref is passed in, that should then be modified
to contain an of your custom fields. Fields which are intended to
receive a single scalar value should have a reference to the
destination scalar given. Fields which are intended to receive
multiple values should reference the corresponding destination
arrayref.
You are responsible for validating your custom options once they have
been parsed. The post_configure_hook is a good place to do your
validation.
Some emails have asked why we use this "template" method. The idea is
that you are creating the the data structure to store the values in,
and you are also creating a way to get the values into the data
structure. The template is the way to get the values to the servers
data structure. One of the possibilities (that probably isn't used
that much) is that by letting you specify the mapping, you could build
a nested data structure - even though the passed in arguments are
flat. It also allows you to setup aliases to your names.
For example, a basic structure might look like this:
$prop = $self->{'server'}
$prop->{'my_custom_option'} ||= undef;
$prop->{'my_custom_array'} ||= [];
$template = {
my_custom_option => \ $prop->{'my_custom_option'},
mco => \ $prop->{'my_custom_option'}, # alias
my_custom_array => $prop->{'my_custom_array'},
mca => $prop->{'my_custom_array'}, # an alias
};
$template->{'mco2'} = $template->{'mco'}; # another way to alias
But you could also have more complex data:
$prop = $self->{'server'};
$prop->{'one_layer'} = {
two_layer => [
undef,
undef,
],
};
$template = {
param1 => \ $prop->{'one_layer'}->{'two_layer'}->[0],
param2 => \ $prop->{'one_layer'}->{'two_layer'}->[1],
};
This is of course a contrived example - but it does show that you can
get the data from the flat passed in arguments to whatever type of
structure you need - with only a little bit of effort.
=head1 DEFAULT ARGUMENTS FOR Net::Server
The following arguments are available in the default C or
C modules. (Other personalities may use
additional parameters and may optionally not use parameters from the
base class.)
Key Value Default
conf_file "filename" undef
log_level 0-4 2
log_file (filename|Sys::Syslog
|Log::Log4perl) undef
port \d+ 20203
host "host" "*"
ipv (4|6|*) *
proto (tcp|udp|unix) "tcp"
listen \d+ SOMAXCONN
## syslog parameters (if log_file eq Sys::Syslog)
syslog_logsock (native|unix|inet|udp
|tcp|stream|console) unix (on Sys::Syslog < 0.15)
syslog_ident "identity" "net_server"
syslog_logopt (cons|ndelay|nowait|pid) pid
syslog_facility \w+ daemon
reverse_lookups 1 undef
allow /regex/ none
deny /regex/ none
cidr_allow CIDR none
cidr_deny CIDR none
## daemonization parameters
pid_file "filename" undef
chroot "directory" undef
user (uid|username) "nobody"
group (gid|group) "nobody"
background 1 undef
setsid 1 undef
no_close_by_child (1|undef) undef
## See Net::Server::Proto::(TCP|UDP|UNIX|SSL|SSLeay|etc)
## for more sample parameters.
=over 4
=item conf_file
Filename from which to read additional key value pair arguments for
starting the server. Default is undef.
There are two ways that you can specify a default location for a
conf_file. The first is to pass the default value to the run method
as in:
MyServer->run({
conf_file => '/etc/my_server.conf',
});
If the end user passes in --conf_file=/etc/their_server.conf then the
value will be overridden.
The second way to do this was added in the 0.96 version. It uses the
default_values method as in:
sub default_values {
return {
conf_file => '/etc/my_server.conf',
}
}
This method has the advantage of also being able to be overridden in
the run method.
If you do not want the user to be able to specify a conf_file at all,
you can pass conf_file to the new method when creating your object:
MyServer->new({
conf_file => '/etc/my_server.conf',
})->run;
If passed this way, the value passed to new will "win" over any of the
other passed in values.
=item log_level
Ranges from 0 to 4 in level. Specifies what level of error will be
logged. "O" means logging is off. "4" means very verbose. These
levels should be able to correlate to syslog levels. Default is 2.
These levels correlate to syslog levels as defined by the following
key/value pairs: 0=>'err', 1=>'warning', 2=>'notice', 3=>'info',
4=>'debug'.
=item log_file
Name of log file or log subsystem to be written to. If no name is given and the
write_to_log_hook is not overridden, log goes to STDERR. Default is
undef.
The log_file may also be the name of a Net::Server pluggable logging
class. Net::Server is packaged with Sys::Syslog and Log::Log4perl.
If the log_file looks like a module name, it will have
"Net::Server::Log::" added to the front and it will then be required.
The package should provide an C class method that returns
a single function which will be used for logging. This returned
function will be passed log_level, and message.
If the magic name "Sys::Syslog" is used, all logging will take place
via the Net::Server::Log::Sys::Syslog module. If syslog is used the
parameters C, C, and
C,and C may also be defined. See
L.
If the magic name "Log::Log4perl" is used, all logging will be
directed to the Log4perl system. If used, the C,
C, C may also be defined. See
L.
If a C is given or if C is set, STDIN and STDOUT
will automatically be opened to /dev/null and STDERR will be opened to
STDOUT. This will prevent any output from ending up at the terminal.
=item pid_file
Filename to store pid of parent process. Generally applies only to
forking servers. Default is none (undef).
=item port
See L for further examples of configuration.
Local port/socket on which to bind. If it is a low port, the process
must start as root. If multiple ports are given, all will be bound at
server startup. May be of the form C,
C, C, C, or C, where
I represents a hostname residing on the local box, where I
represents either the number of the port (eg. "80") or the service
designation (eg. "http"), where I represents the IP protocol version
(IPv4 or IPv6 or IPv*) and where I represents the protocol to be
used. See L. The following are some valid port
strings:
20203 # port only
localhost:20203 # host and port
localhost:http # localhost bound to port 80
localhost:20203/tcp # host, port, protocol
localhost:20203/tcp/IPv* # host, port, protocol and family
localhost, 20203, tcp, IPv* # same
localhost | 20203 | tcp | IPv* # same
localhost:20203/IPv* # bind any configured interfaces for IPv4 or 6 (default)
localhost:20203/IPv4/IPv6 # bind localhost on IPv4 and 6 (fails if it cannot do both)
*:20203 # bind all local interfaces
Additionally, when passed in the code (non-commandline, and non-config),
the port may be passed as a hashref or array hashrefs of information:
port => {
host => 'localhost',
port => '20203',
ipv => 6, # IPv6 only
proto => 'udp', # UDP protocol
}
port => [{
host => '*',
port => '20203',
ipv => 4, # IPv4 only
proto => 'tcp', # (default)
}, {
host => 'localhost',
port => '20204',
ipv => '*', # default - all IPv4 and IPv6 interfaces tied to localhost
proto => 'ssleay', # or ssl - Using SSL
}],
An explicit I given in a port specification overrides a default
binding address (a C setting, see below). The I part may
be enclosed in square brackets, but when it is a numerical IPv6
address it B be enclosed in square brackets to avoid ambiguity
in parsing a port number, e.g.: "[::1]:80". However you could also
use pipes, white space, or commas to separate these. Note that host
and port number must come first.
If the protocol is not specified, I will default to the
C specified in the arguments. If C is not specified
there it will default to "tcp". If I is not specified, I
will default to C specified in the arguments. If C is not
specified there it will default to "*". Default port is 20203.
Configuration passed to new or run may be either a scalar containing a
single port number or an arrayref of ports. If C is not specified
it will default to "*" (Any resolved addresses under IPv4 or IPv6).
If you are working with unix sockets, you may also specify
C or C where type is SOCK_DGRAM
or SOCK_STREAM.
On systems that support it, a port value of 0 may be used to ask
the OS to auto-assign a port. The value of the auto-assigned port
will be stored in the NS_port property of the Net::Server::Proto::TCP
object and is also available in the sockport method. When the server
is processing a request, the $self->{server}->{sockport} property
contains the port that was connected through.
=item host
Local host or addr upon which to bind port. If a value of '*' is
given, the server will bind that port on all available addresses on
the box. The C argument provides a default local host address
if the C argument omits a host specification. See
L. See L. Configuration passed to new
or run may be either a scalar containing a single host or an arrayref
of hosts - if the hosts array is shorter than the ports array, the
last host entry will be used to augment the hosts arrary to the size
of the ports array.
If an IPv4 address is passed, an IPv4 socket will be created. If an
IPv6 address is passed, an IPv6 socket will be created. If a hostname
is given, Net::Server will look at the value of ipv (default IPv4) to
determine which type of socket to create. Optionally the ipv
specification can be passed as part of the hostname.
host => "127.0.0.1", # an IPv4 address
host => "::1", # an IPv6 address
host => 'localhost', # addresses matched by localhost (default any IPv4 and/or IPv6)
host => 'localhost/IPv*', # same
ipv => 6,
host => 'localhost', # addresses matched by localhost (IPv6)
ipv => 4,
host => 'localhost', # addresses matched by localhost (IPv4)
ipv => 'IPv4 IPv6',
host => 'localhost', # addresses matched by localhost (requires IPv6 and IPv4)
host => '*', # any local interfaces (any IPv6 or IPv4)
host => '*/IPv*', # same (any IPv6 or IPv4)
ipv => 4,
host => '*', # any local IPv4 interfaces interfaces
=item proto
See L.
Protocol to use when binding ports. See L. As of release
2.0, Net::Server supports tcp, udp, and unix, unixdgram, ssl, and
ssleay. Other types will need to be added later (or custom modules
extending the Net::Server::Proto class may be used). Configuration
passed to new or run may be either a scalar containing a single proto
or an arrayref of protos - if the protos array is shorter than the
ports array, the last proto entry will be used to augment the protos
arrary to the size of the ports array.
Additionally the proto may also contain the ipv specification.
=item ipv (IPv4 and IPv6)
See L.
IPv6 is now available under Net::Server. It will be used
automatically if an IPv6 address is passed, or if the ipv is set
explicitly to IPv6, or if ipv is left as the default value of IPv*.
This is a significant change from version 2.004 and earlier where the
default value was IPv4. However, the previous behavior led to
confusion on IPv6 only hosts, and on hosts that only had IPv6 entries
for a local hostname. Trying to pass an IPv4 address when ipv is set
to 6 (only 6 - not * or 4) will result in an error.
localhost:20203 # will use IPv6 if there is a corresponding entry for localhost
# it will also use IPv4 if there is a corresponding v4 entry for localhost
localhost:20203:IPv* # same (default)
localhost:20203:IPv6 # will use IPv6
[::1]:20203 # will use IPv6 (IPv6 style address)
localhost:20203:IPv4 # will use IPv4
127.0.0.1:20203 # will use IPv4 (IPv4 style address
localhost:20203:IPv4:IPv6 # will bind to both v4 and v6 - fails otherwise
# or as a hashref as
port => {
host => "localhost",
ipv => 6, # only binds IPv6
}
port => {
host => "localhost",
ipv => 4, # only binds IPv4
}
port => {
host => "::1",
ipv => "IPv6", # same as passing "6"
}
port => {
host => "localhost/IPv*", # any IPv4 or IPv6
}
port => {
host => "localhost IPv4 IPv6", # must create both
}
In many proposed Net::Server solutions, IPv* was enabled by default.
For versions 2.000 through 2.004, the previous default of IPv4 was
used. We have attempted to make it easy to set IPv4, IPv6, or IPv*.
If you do not want or need IPv6, simply set ipv to 4, pass IPv4 along
in the port specification, set $ENV{'IPV'}=4; before running the
server, or uninstall IO::Socket::INET6.
On my local box the following command results in the following output:
perl -e 'use base qw(Net::Server); main->run(host => "localhost")'
Resolved [localhost]:20203 to [::1]:20203, IPv6
Resolved [localhost]:20203 to [127.0.0.1]:20203, IPv4
Binding to TCP port 20203 on host ::1 with IPv6
Binding to TCP port 20203 on host 127.0.0.1 with IPv4
My local box has IPv6 enabled and there are entries for localhost on
both IPv6 ::1 and IPv4 127.0.0.1. I could also choose to explicitly
bind ports rather than depending upon ipv => "*" to resolve them for
me as in the following:
perl -e 'use base qw(Net::Server); main->run(port => [20203,20203], host => "localhost", ipv => [4,6])'
Binding to TCP port 20203 on host localhost with IPv4
Binding to TCP port 20203 on host localhost with IPv6
There is a special case of using host => "*" as well as ipv => "*".
The Net::Server::Proto::_bindv6only method is used to check the system
setting for C (or
net.inet6.ip6.v6only). If this setting is false, then an IPv6 socket
will listen for the corresponding IPv4 address. For example the
address [::] (IPv6 equivalent of INADDR_ANY) will also listen for
0.0.0.0. The address ::FFFF:127.0.0.1 (IPv6) would also listen to
127.0.0.1 (IPv4). In this case, only one socket will be created
because it will handle both cases (an error is returned if an attempt
is made to listen to both addresses when bindv6only is false).
However, if net.ipv6.bindv6only (or equivalent) is true, then a
hostname (such as *) resolving to both a IPv4 entry as well as an IPv6
will result in both an IPv4 socket as well as an IPv6 socket.
On my linux box which defaults to net.ipv6.bindv6only=0, the following is output.
perl -e 'use base qw(Net::Server); main->run(host => "*")'
Resolved [*]:8080 to [::]:8080, IPv6
Not including resolved host [0.0.0.0] IPv4 because it will be handled by [::] IPv6
Binding to TCP port 8080 on host :: with IPv6
If I issue a C, the following is output.
perl -e 'use base qw(Net::Server); main->run(host => "*")'
Resolved [*]:8080 to [0.0.0.0]:8080, IPv4
Resolved [*]:8080 to [::]:8080, IPv6
Binding to TCP port 8080 on host 0.0.0.0 with IPv4
Binding to TCP port 8080 on host :: with IPv6
BSD differs from linux and generally defaults to
net.inet6.ip6.v6only=0. If it cannot be determined on your OS, it
will default to false and the log message will change from "it will be
handled" to "it should be handled" (if you have a non-resource
intensive way to check on your platform, feel free to email me). Be
sure to check the logs as you test your server to make sure you have
bound the ports you desire. You can always pass in individual
explicit IPv4 and IPv6 port specifications if you need. For example,
if your system has both IPv4 and IPv6 interfaces but you'd only like
to bind to IPv6 entries, then you should use a hostname of [::]
instead of [*].
If bindv6only (or equivalent) is false, and you receive an IPv4
connection on a bound IPv6 port, the textual representation of the
peer's IPv4 address will typically be in a form of an IPv4-mapped IPv6
addresses, e.g. "::FFFF:127.0.0.1" .
The ipv parameter was chosen because it does not conflict with any
other existing usage, it is very similar to ipv4 or ipv6, it allows
for user code to not need to know about Socket::AF_INET or
Socket6::AF_INET6 or Socket::AF_UNSPEC, and it is short.
=item listen
See L. Not used with udp protocol (or UNIX SOCK_DGRAM).
=item reverse_lookups
Specify whether to lookup the hostname of the connected IP.
Information is cached in server object under C property.
Default is to not use reverse_lookups (undef).
=item allow/deny
May be specified multiple times. Contains regex to compare to
incoming peeraddr or peerhost (if reverse_lookups has been enabled).
If allow or deny options are given, the incoming client must match an
allow and not match a deny or the client connection will be closed.
Defaults to empty array refs.
=item cidr_allow/cidr_deny
May be specified multiple times. Contains a CIDR block to compare to
incoming peeraddr. If cidr_allow or cidr_deny options are given, the
incoming client must match a cidr_allow and not match a cidr_deny or
the client connection will be closed. Defaults to empty array refs.
=item chroot
Directory to chroot to after bind process has taken place and the
server is still running as root. Defaults to undef.
=item user
Userid or username to become after the bind process has occured.
Defaults to "nobody." If you would like the server to run as root,
you will have to specify C equal to "root".
=item group
Groupid or groupname to become after the bind process has occured.
Defaults to "nobody." If you would like the server to run as root,
you will have to specify C equal to "root".
=item background
Specifies whether or not the server should fork after the bind method
to release itself from the command line. Defaults to undef. Process
will also background if C is set.
=item setsid
Specifies whether or not the server should fork after the bind method
to release itself from the command line and then run the
C command to truly daemonize. Defaults to undef. If
a C is given or if C is set, STDIN and STDOUT will
automatically be opened to /dev/null and STDERR will be opened to
STDOUT. This will prevent any output from ending up at the terminal.
=item no_close_by_child
Boolean. Specifies whether or not a forked child process has
permission or not to shutdown the entire server process. If set to 1,
the child may NOT signal the parent to shutdown all children. Default
is undef (not set).
=item no_client_stdout
Boolean. Default undef (not set). Specifies that STDIN and STDOUT
should not be opened on the client handle once a connection has been
accepted. By default the Net::Server will open STDIN and STDOUT on
the client socket making it easier for many types of scripts to read
directly from and write directly to the socket using normal print and
read methods. Disabling this is useful on clients that may be opening
their own connections to STDIN and STDOUT.
This option has no affect on STDIN and STDOUT which has a magic client
property that is tied to the already open STDIN and STDOUT.
=item leave_children_open_on_hup
Boolean. Default undef (not set). If set, the parent will not
attempt to close child processes if the parent receives a SIG HUP.
The parent will rebind the the open port and begin tracking a fresh
set of children.
Children of a Fork server will exit after their current request.
Children of a Prefork type server will finish the current request and
then exit.
Note - the newly restarted parent will start up a fresh set of servers
on fork servers. The new parent will attempt to keep track of the
children from the former parent but custom communication channels
(open pipes from the child to the old parent) will no longer be
available to the old child processes. New child processes will still
connect properly to the new parent.
=item sig_passthrough
Default none. Allow for passing requested signals through to
children. Takes a single signal name, a comma separated list of
names, or an arrayref of signal names. It first sends the signals to
the children before calling any currently registered signal by that
name.
=item tie_client_stdout
Default undef. If set will use Net::Server::TiedHandle tied interface
for STDIN and STDOUT. This interface allows SSL and SSLEAY to work.
It also allows for intercepting read and write via the
tied_stdin_callback and tied_stdout_callback.
=item tied_stdin_callback
Default undef. Called during a read of STDIN data if
tie_client_stdout has been set, or if the client handle's tie_stdout
method returns true. It is passed the client connection, the name of
the method that would be called, and the arguments that are being
passed. The callback is then responsible for calling that method on
the handle or for performing some other input operation.
=item tied_stdout_callback
Default undef. Called during a write of data to STDOUT if
tie_client_stdout has been set, or if the client handle's tie_stdout
method returns true. It is passed the client connection, the name of
the method that would be called, and the arguments that are being
passed. The callback is then responsible for calling that method on
the handle or for performing some other output operation.
=back
=head1 PROPERTIES
All of the C listed above become properties of the server
object under the same name. These properties, as well as other
internal properties, are available during hooks and other method
calls.
The structure of a Net::Server object is shown below:
$self = bless({
server => {
key1 => 'val1',
# more key/vals
},
}, 'Net::Server');
This structure was chosen so that all server related properties are
grouped under a single key of the object hashref. This is so that
other objects could layer on top of the Net::Server object class and
still have a fairly clean namespace in the hashref.
You may get and set properties in two ways. The suggested way is to
access properties directly via
my $val = $self->{server}->{key1};
Accessing the properties directly will speed the server process -
though some would deem this as bad style. A second way has been
provided for object oriented types who believe in methods. The second
way consists of the following methods:
my $val = $self->get_property( 'key1' );
my $self->set_property( key1 => 'val1' );
Properties are allowed to be changed at any time with caution (please
do not undef the sock property or you will close the client
connection).
=head1 CONFIGURATION FILE
C allows for the use of a configuration file to read in
server parameters. The format of this conf file is simple key value
pairs. Comments and blank lines are ignored.
#-------------- file test.conf --------------
### user and group to become
user somebody
group everybody
# logging ?
log_file /var/log/server.log
log_level 3
pid_file /tmp/server.pid
# optional syslog directive
# used in place of log_file above
#log_file Sys::Syslog
#syslog_logsock unix
#syslog_ident myserver
#syslog_logopt pid|cons
# access control
allow .+\.(net|com)
allow domain\.com
deny a.+
cidr_allow 127.0.0.0/8
cidr_allow 192.0.2.0/24
cidr_deny 192.0.2.4/30
# background the process?
background 1
# ports to bind (this should bind
# 127.0.0.1:20205 on IPv6 and
# localhost:20204 on IPv4)
# See Net::Server::Proto
host 127.0.0.1
ipv IPv6
port localhost:20204/IPv4
port 20205
# reverse lookups ?
# reverse_lookups on
#-------------- file test.conf --------------
=head1 PROCESS FLOW
The process flow is written in an open, easy to
override, easy to hook, fashion. The basic flow is
shown below. This is the flow of the C<$self-Erun> method.
$self->configure_hook;
$self->configure(@_);
$self->post_configure;
$self->post_configure_hook;
$self->pre_bind;
$self->bind;
$self->post_bind_hook;
$self->post_bind;
$self->pre_loop_hook;
$self->loop;
### routines inside a standard $self->loop
# $self->accept;
# $self->run_client_connection;
# $self->done;
$self->pre_server_close_hook;
$self->server_close;
The server then exits.
During the client processing phase
(C<$self-Erun_client_connection>), the following
represents the program flow:
$self->post_accept;
$self->get_client_info;
$self->post_accept_hook;
if ($self->allow_deny
&& $self->allow_deny_hook) {
$self->process_request;
} else {
$self->request_denied_hook;
}
$self->post_process_request_hook;
$self->post_process_request;
$self->post_client_connection_hook;
The process then loops and waits for the next connection. For a more
in depth discussion, please read the code.
During the server shutdown phase (C<$self-Eserver_close>), the
following represents the program flow:
$self->close_children; # if any
$self->post_child_cleanup_hook;
if (Restarting server) {
$self->restart_close_hook();
$self->hup_server;
}
$self->shutdown_sockets;
$self->server_exit;
=head1 MAIN SERVER METHODS
=over 4
=item C<$self-Erun>
This method incorporates the main process flow. This flow is listed
above.
The method run may be called in any of the following ways.
MyPackage->run(port => 20201);
MyPackage->new({port => 20201})->run;
my $obj = bless {server=>{port => 20201}}, 'MyPackage';
$obj->run;
The ->run method should typically be the last method called in a
server start script (the server will exit at the end of the ->run
method).
=item C<$self-Econfigure>
This method attempts to read configurations from the commandline, from
the run method call, or from a specified conf_file (the conf_file may
be specified by passed in parameters, or in the default_values). All
of the configured parameters are then stored in the {"server"}
property of the Server object.
=item C<$self-Epost_configure>
The post_configure hook begins the startup of the server. During this
method running server instances are checked for, pid_files are
created, log_files are created, Sys::Syslog is initialized (as
needed), process backgrounding occurs and the server closes STDIN and
STDOUT (as needed).
=item C<$self-Epre_bind>
This method is used to initialize all of the socket objects used by
the server.
=item C<$self-Ebind>
This method actually binds to the inialized sockets (or rebinds if the
server has been HUPed).
=item C<$self-Epost_bind>
During this method priveleges are dropped. The INT, TERM, and QUIT
signals are set to run server_close. Sig PIPE is set to IGNORE. Sig
CHLD is set to sig_chld. And sig HUP is set to call sig_hup.
Under the Fork, PreFork, and PreFork simple personalities, these
signals are registered using Net::Server::SIG to allow for safe signal
handling.
=item C<$self-Eloop>
During this phase, the server accepts incoming connections. The
behavior of how the accepting occurs and if a child process handles
the connection is controlled by what type of Net::Server personality
the server is using.
Net::Server and Net::Server single accept only one connection at a
time.
Net::Server::INET runs one connection and then exits (for use by inetd
or xinetd daemons).
Net::Server::MultiPlex allows for one process to simultaneously handle
multiple connections (but requires rewriting the process_request code
to operate in a more "packet-like" manner).
Net::Server::Fork forks off a new child process for each incoming
connection.
Net::Server::PreForkSimple starts up a fixed number of processes that
all accept on incoming connections.
Net::Server::PreFork starts up a base number of child processes which
all accept on incoming connections. The server throttles the number
of processes running depending upon the number of requests coming in
(similar to concept to how Apache controls its child processes in a
PreFork server).
Read the documentation for each of the types for more information.
=item C<$self-Eserver_close>
This method is called once the server has been signaled to end, or
signaled for the server to restart (via HUP), or the loop method has
been exited.
This method takes care of cleaning up any remaining child processes,
setting appropriate flags on sockets (for HUPing), closing up logging,
and then closing open sockets.
Can optionally be passed an exit value that will be passed to the
server_exit call.
=item C<$self-Eserver_exit>
This method is called at the end of server_close. It calls exit, but
may be overridden to do other items. At this point all services
should be shut down.
Can optionally be passed an exit value that will be passed to the exit call.
=back
=head1 MAIN CLIENT CONNECTION METHODS
=over 4
=item C<$self-Erun_client_connection>
This method is run after the server has accepted and received a client
connection. The full process flow is listed above under PROCESS
FLOWS. This method takes care of handling each client connection.
=item C<$self-Epost_accept>
This method opens STDIN and STDOUT to the client socket. This allows
any of the methods during the run_client_connection phase to print
directly to and read directly from the client socket.
=item C<$self-Eget_client_info>
This method looks up information about the client connection such as
ip address, socket type, and hostname (as needed).
=item C<$self-Eallow_deny>
This method uses the rules defined in the allow and deny configuration
parameters to determine if the ip address should be accepted.
=item C<$self-Eprocess_request>
This method is intended to handle all of the client communication. At
this point STDIN and STDOUT are opened to the client, the ip address
has been verified. The server can then interact with the client
connection according to whatever API or protocol the server is
implementing. Note that the stub implementation uses STDIN and STDOUT
and will not work if the no_client_stdout flag is set.
This is the main method to override.
The default method implements a simple echo server that will repeat
whatever is sent. It will quit the child if "quit" is sent, and will
exit the server if "exit" is sent.
As of version 2.000, the client handle is passed as an argument.
=item C<$self-Epost_process_request>
This method is used to clean up the client connection and to handle
any parent/child accounting for the forking servers.
=back
=head1 HOOKS
C provides a number of "hooks" allowing for servers
layered on top of C to respond at different levels of
execution without having to "SUPER" class the main built-in methods.
The placement of the hooks can be seen in the PROCESS FLOW section.
Almost all of the default hook methods do nothing. To use a hook you
simply need to override the method in your subclass. For example to
add your own post_configure_hook you could do something like the
following:
package MyServer;
sub post_configure_hook {
my $self = shift;
my $prop = $self->{'server'};
# do some validation here
}
The following describes the hooks available in the plain Net::Server
class (other flavors such as Fork or PreFork have additional hooks).
=over 4
=item C<$self-Econfigure_hook()>
This hook takes place immediately after the C<-Erun()> method is
called. This hook allows for setting up the object before any built
in configuration takes place. This allows for custom configurability.
=item C<$self-Epost_configure_hook()>
This hook occurs just after the reading of configuration parameters
and initiation of logging and pid_file creation. It also occurs
before the C<-Epre_bind()> and C<-Ebind()> methods are called.
This hook allows for verifying configuration parameters.
=item C<$self-Epost_bind_hook()>
This hook occurs just after the bind process and just before any
chrooting, change of user, or change of group occurs. At this point
the process will still be running as the user who started the server.
=item C<$self-Epre_loop_hook()>
This hook occurs after chroot, change of user, and change of group has
occured. It allows for preparation before looping begins.
=item C<$self-Ecan_read_hook()>
This hook occurs after a socket becomes readible on an
accept_multi_port request (accept_multi_port is used if there are
multiple bound ports to accept on, or if the "multi_port"
configuration parameter is set to true). This hook is intended to
allow for processing of arbitrary handles added to the IO::Select used
for the accept_multi_port. These handles could be added during the
post_bind_hook. No internal support is added for processing these
handles or adding them to the IO::Socket. Care must be used in how
much occurs during the can_read_hook as a long response time will
result in the server being susceptible to DOS attacks. A return value
of true indicates that the Server should not pass the readible handle
on to the post_accept and process_request phases.
It is generally suggested that other avenues be pursued for sending
messages via sockets not created by the Net::Server.
=item C<$self-Epost_accept_hook()>
This hook occurs after a client has connected to the server. At this
point STDIN and STDOUT are mapped to the client socket. This hook
occurs before the processing of the request.
=item C<$self-Eallow_deny_hook()>
This hook allows for the checking of ip and host information beyond
the C<$self-Eallow_deny()> routine. If this hook returns 1, the
client request will be processed, otherwise, the request will be
denied processing.
As of version 2.000, the client connection is passed as an argument.
=item C<$self-Erequest_denied_hook()>
This hook occurs if either the C<$self-Eallow_deny()> or
C<$self-Eallow_deny_hook()> have taken place.
=item C<$self-Epost_process_request_hook()>
This hook occurs after the processing of the request, but before the
client connection has been closed.
=item C<$self-Epost_client_connection_hook>
This is one final hook that occurs at the very end of the
run_client_connection method. At this point all other methods and
hooks that will run during the run_client_connection have finished and
the client connection has already been closed.
item C<$self-Eother_child_died_hook($pid)>
Net::Server takes control of signal handling and child process
cleanup; this makes it difficult to tell when a child process
terminates if that child process was not started by Net::Server
itself. If Net::Server notices another child process dying that it
did not start, it will fire this hook with the PID of the terminated
process.
=item C<$self-Epre_server_close_hook()>
This hook occurs before the server begins shutting down.
=item C<$self-Ewrite_to_log_hook>
This hook handles writing to log files. The default hook is to write
to STDERR, or to the filename contained in the parameter C.
The arguments passed are a log level of 0 to 4 (4 being very verbose),
and a log line. If log_file is equal to "Sys::Syslog", then logging
will go to Sys::Syslog and will bypass the write_to_log_hook.
=item C<$self-Efatal_hook>
This hook occurs when the server has encountered an unrecoverable
error. Arguments passed are the error message, the package, file, and
line number. The hook may close the server, but it is suggested that
it simply return and use the built in shut down features.
=item C<$self-Epost_child_cleanup_hook>
This hook occurs in the parent server process after all children have
been shut down and just before the server either restarts or exits.
It is intended for additional cleanup of information. At this point
pid_files and lockfiles still exist.
=item C<$self-Erestart_open_hook>
This hook occurs if a server has been HUPed (restarted via the HUP
signal. It occurs just before reopening to the filenos of the sockets
that were already opened.
=item C<$self-Erestart_close_hook>
This hook occurs if a server has been HUPed (restarted via the HUP
signal. It occurs just before restarting the server via exec.
=item C<$self-Echild_init_hook()>
This hook is called during the forking servers. It is also called
during run_dequeue. It runs just after the fork and after signals
have been cleaned up. If it is a dequeue process, the string
'dequeue' will be passed as an argument.
If your child processes will be needing random numbers, this
hook is a good location to initialize srand (forked processes
maintain the same random seed unless changed).
sub child_init_hook {
# from perldoc -f srand
srand(time ^ $$ ^ unpack "%L*", `ps axww | gzip -f`);
}
=item C<$self-Epre_fork_hook()>
Similar to the child_init_hook, but occurs just before the fork.
=item C<$self-Echild_finish_hook()>
Similar to the child_init_hook, but ran when the forked process is
about to finish up.
=back
=head1 OTHER METHODS
=over 4
=item C<$self-Edefault_values>
Allow for returning configuration values that will be used if no other
value could be found.
Should return a hashref.
sub default_values {
return {
port => 20201,
};
}
=item C<$self-Ehandle_syslog_error>
Called when log_file is set to 'Sys::Syslog' and an error occurs while
writing to the syslog. It is passed two arguments, the value of $@,
and an arrayref containing the arguments that were passed to the log
method when the error occured.
=item C<$self-Elog>
Parameters are a log_level and a message.
If log_level is set to 'Sys::Syslog', the parameters may alternately
be a log_level, a format string, and format string parameters. (The
second parameter is assumed to be a format string if additional
arguments are passed along). Passing arbitrary format strings to
Sys::Syslog will allow the server to be vulnerable to exploit. The
server maintainer should make sure that any string treated as a format
string is controlled.
# assuming log_file = 'Sys::Syslog'
$self->log(1, "My Message with %s in it");
# sends "%s", "My Message with %s in it" to syslog
$self->log(1, "My Message with %s in it", "Foo");
# sends "My Message with %s in it", "Foo" to syslog
If log_file is set to a file (other than Sys::Syslog), the message
will be appended to the log file by calling the write_to_log_hook.
If the log_file is Sys::Syslog and an error occurs during write, the
handle_syslog_error method will be called and passed the error
exception. The default option of handle_syslog_error is to die - but
could easily be told to do nothing by using the following code in your
subclassed server:
sub handle_syslog_error {}
It the log had been closed, you could attempt to reopen it in the
error handler with the following code:
sub handle_syslog_error {
my $self = shift;
$self->open_syslog;
}
=item C<$self-Enew>
As of Net::Server 0.91 there is finally a "new" method. This method
takes a class name and an argument hashref as parameters. The
argument hashref becomes the "server" property of the object.
package MyPackage;
use base qw(Net::Server);
my $obj = MyPackage->new({port => 20201});
# same as
my $obj = bless {server => {port => 20201}}, 'MyPackage';
=item C<$self-Eopen_syslog>
Called during post_configure when the log_file option is set to
'Sys::Syslog'. By default it use the parsed configuration options
listed in this document. If more custom behavior is desired, the
method could be overridden and Sys::Syslog::openlog should be called
with the custom parameters.
=item C<$self-Eshutdown_sockets>
This method will close any remaining open sockets. This is called at
the end of the server_close method.
=back
=head1 RESTARTING
Each of the server personalities (except for INET), support restarting
via a HUP signal (see "kill -l"). When a HUP is received, the server
will close children (if any), make sure that sockets are left open,
and re-exec using the same commandline parameters that initially
started the server. (Note: for this reason it is important that @ARGV
is not modified until C<-Erun> is called).
The Net::Server will attempt to find out the commandline used for
starting the program. The attempt is made before any configuration
files or other arguments are processed. The outcome of this attempt
is stored using the method C<-Ecommandline>. The stored
commandline may also be retrieved using the same method name. The
stored contents will undoubtedly contain Tainted items that will cause
the server to die during a restart when using the -T flag (Taint
mode). As it is impossible to arbitrarily decide what is taint safe
and what is not, the individual program must clean up the tainted
items before doing a restart.
sub configure_hook{
my $self = shift;
### see the contents
my $ref = $self->commandline;
use Data::Dumper;
print Dumper $ref;
### arbitrary untainting - VERY dangerous
my @untainted = map {/(.+)/;$1} @$ref;
$self->commandline(\@untainted)
}
=head1 SHUTDOWN
Each of the Fork and PreFork personalities support graceful shutdowns
via the QUIT signal. When a QUIT is received, the parent will signal
the children and then wait for them to exit.
All server personalities support the normal TERM and INT signal
shutdowns.
=head1 HOT DEPLOY
Since version 2.000, the Fork and PreFork personalities have accepted
the TTIN and TTOU signals. When a TTIN is received, the max_servers
is increased by 1. If a TTOU signal is received the max_servers is
decreased by 1. This allows for adjusting the number of handling
processes without having to restart the server.
If the log_level is set to at 3, then the new value is displayed in
the logs.
=head1 FILES
The following files are installed as part of this distribution.
Net/Server.pm
Net/Server/Fork.pm
Net/Server/INET.pm
Net/Server/MultiType.pm
Net/Server/PreForkSimple.pm
Net/Server/PreFork.pm
Net/Server/Single.pm
Net/Server/Daemonize.pm
Net/Server/SIG.pm
Net/Server/Proto.pm
Net/Server/Proto/*.pm
=head1 INSTALL
Download and extract tarball before running these commands in its base
directory:
perl Makefile.PL
make
make test
make install
=head1 AUTHOR
Paul Seamons
=head1 THANKS
Thanks to Rob Brown (bbb at cpan.org) for help with miscellaneous
concepts such as tracking down the serialized select via flock ala
Apache and the reference to IO::Select making multiport servers
possible. And for researching into allowing sockets to remain open
upon exec (making HUP possible).
Thanks to Jonathan J. Miner for patching a
blatant problem in the reverse lookups.
Thanks to Bennett Todd for pointing out a problem
in Solaris 2.5.1 which does not allow multiple children to accept on
the same port at the same time. Also for showing some sample code
from Viktor Duchovni which now represents the semaphore option of the
serialize argument in the PreFork server.
Thanks to I and I from http://perlmonks.org for
pointing me in the right direction for determining the protocol used
on a socket connection.
Thanks to Jeremy Howard for numerous
suggestions and for work on Net::Server::Daemonize.
Thanks to Vadim for patches to implement
parent/child communication on PreFork.pm.
Thanks to Carl Lewis for suggesting "-" in user names.
Thanks to Slaven Rezic for suggesing Reuse => 1 in Proto::UDP.
Thanks to Tim Watt for adding udp_broadcast to Proto::UDP.
Thanks to Christopher A Bongaarts for pointing out problems with the
Proto::SSL implementation that currently locks around the socket
accept and the SSL negotiation. See L.
Thanks to Alessandro Zummo for pointing out various bugs including
some in configuration, commandline args, and cidr_allow.
Thanks to various other people for bug fixes over the years. These
and future thank-you's are available in the Changes file as well as
CVS comments.
Thanks to Ben Cohen and tye (on Permonks) for finding and diagnosing
more correct behavior for dealing with re-opening STDIN and STDOUT on
the client handles.
Thanks to Mark Martinec for trouble shooting other problems with STDIN
and STDOUT (he proposed having a flag that is now the no_client_stdout
flag).
Thanks to David (DSCHWEI) on cpan for asking for the nofatal option
with syslog.
Thanks to Andreas Kippnick and Peter Beckman for suggesting leaving
open child connections open during a HUP (this is now available via
the leave_children_open_on_hup flag).
Thanks to LUPE on cpan for helping patch HUP with taint on.
Thanks to Michael Virnstein for fixing a bug in the check_for_dead
section of PreFork server.
Thanks to Rob Mueller for patching PreForkSimple to only open
lock_file once during parent call. This patch should be portable on
systems supporting flock. Rob also suggested not closing STDIN/STDOUT
but instead reopening them to /dev/null to prevent spurious warnings.
Also suggested short circuit in post_accept if in UDP. Also for
cleaning up some of the child managment code of PreFork.
Thanks to Mark Martinec for suggesting additional log messages for
failure during accept.
Thanks to Bill Nesbitt and Carlos Velasco for pointing out double
decrement bug in PreFork.pm (rt #21271)
Thanks to John W. Krahn for pointing out glaring precended with
non-parened open and ||.
Thanks to Ricardo Signes for pointing out setuid bug for perl 5.6.1
(rt #21262).
Thanks to Carlos Velasco for updating the Syslog options (rt #21265).
And for additional fixes later.
Thanks to Steven Lembark for pointing out that no_client_stdout wasn't
working with the Multiplex server.
Thanks to Peter Beckman for suggesting allowing Sys::SysLog keyworks
be passed through the ->log method and for suggesting we allow more
types of characters through in syslog_ident. Also to Peter Beckman
for pointing out that a poorly setup localhost will cause tests to
hang.
Thanks to Curtis Wilbar for pointing out that the Fork server called
post_accept_hook twice. Changed to only let the child process call
this, but added the pre_fork_hook method.
And just a general Thanks You to everybody who is using Net::Server or
who has contributed fixes over the years.
Thanks to Paul Miller for some ->autoflush, FileHandle fixes.
Thanks to Patrik Wallstrom for suggesting handling syslog errors
better.
Thanks again to Rob Mueller for more logic cleanup for child
accounting in PreFork server.
Thanks to David Schweikert for suggesting handling setlogsock a little
better on newer versions of Sys::Syslog (>= 0.15).
Thanks to Mihail Nasedkin for suggesting adding a hook that is now
called post_client_connection_hook.
Thanks to Graham Barr for adding the ability to set the
check_for_spawn and min_child_ttl settings of the PreFork server.
Thanks to Daniel Kahn Gillmor for adding the other_child_died_hook.
Thanks to Dominic Humphries for helping not kill pid files on HUP.
Thanks to Kristoffer Møllerhøj for fixing UDP on Multiplex.
Thanks to mishikal for patches for helping identify un-cleaned up
children.
Thanks to rpkelly and tim@retout for pointing out error in header
regex of HTTP.
Thanks to dmcbride for some basic HTTP parsing fixes, as well as for
some broken tied handle fixes.
Thanks to Gareth for pointing out glaring bug issues with broken pipe
and semaphore serialization.
Thanks to CATONE for sending the idea for arbitrary signal passing to
children. (See the sig_passthrough option)
Thanks to intrigeri@boum for pointing out and giving code ideas for
NS_port not functioning after a HUP.
Thanks to Sergey Zasenko for adding sysread/syswrite support to SSLEAY
as well as the base test.
Thanks to mbarbon@users. for adding tally dequeue to prefork server.
Thanks to stefanos@cpan for fixes to PreFork under Win32
Thanks to Mark Martinec for much of the initial work towards getting
IPv6 going.
Thanks to the munin developers and Nicolai Langfeldt for hosting the
development verion of Net::Server for so long and for fixes to the
allow_deny checking for IPv6 addresses.
Thanks to Tatsuhiko Miyagawa for feedback, and for suggesting adding
graceful shutdowns and hot deploy (max_servers adjustment).
Thanks to TONVOON@cpan for submitting a patch adding Log4perl functionality.
Thanks to Miko O'Sullivan for fixes to HTTP to correct tainting issues
and passing initial log fixes, and for patches to fix CLOSE on tied stdout
and various other HTTP issues.
=head1 SEE ALSO
Please see also
L,
L,
L,
L,
L,
L
L
=head1 TODO
Improve test suite to fully cover code (using Devel::Cover). Anybody
that wanted to send me patches to the t/*.t tests that improved
coverage would earn a big thank you.
=head1 CODE REPOSITORY
https://github.com/rhandom/perl-net-server
=head1 AUTHOR
Paul Seamons
http://seamons.com/
Rob Brown
=head1 LICENSE
This package may be distributed under the terms of either the
GNU General Public License
or the
Perl Artistic License
All rights reserved.
=cut
Net-Server-2.008/lib/Net/Server.pm 0000644 0001750 0001750 00000113675 12334164757 015332 0 ustar paul paul # -*- perl -*-
#
# Net::Server
# ABSTRACT: Extensible Perl internet server
#
# Copyright (C) 2001-2014
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# Rob Brown bbb@cpan,org
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
# All rights reserved.
#
################################################################
package Net::Server;
use strict;
use Socket qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM);
use IO::Socket ();
use IO::Select ();
use POSIX ();
use Net::Server::Proto ();
use Net::Server::Daemonize qw(check_pid_file create_pid_file safe_fork
get_uid get_gid set_uid set_gid);
our $VERSION = '2.008';
sub new {
my $class = shift || die "Missing class";
my $args = @_ == 1 ? shift : {@_};
return bless {server => {%$args}}, $class;
}
sub net_server_type { __PACKAGE__ }
sub get_property { $_[0]->{'server'}->{$_[1]} }
sub set_property { $_[0]->{'server'}->{$_[1]} = $_[2] }
sub run {
my $self = ref($_[0]) ? shift() : shift->new; # pass package or object
$self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_];
$self->_initialize; # configure all parameters
$self->post_configure; # verification of passed parameters
$self->post_configure_hook; # user customizable hook
$self->pre_bind; # finalize ports to be bound
$self->bind; # connect to port(s), setup selection handle for multi port
$self->post_bind_hook; # user customizable hook
$self->post_bind; # allow for chrooting, becoming a different user and group
$self->pre_loop_hook; # user customizable hook
$self->loop; # repeat accept/process cycle
$self->server_close; # close the server and release the port
}
sub run_client_connection {
my $self = shift;
my $c = $self->{'server'}->{'client'};
$self->post_accept($c); # prepare client for processing
$self->get_client_info($c); # determines information about peer and local
$self->post_accept_hook($c); # user customizable hook
my $ok = $self->allow_deny($c) && $self->allow_deny_hook($c); # do allow/deny check on client info
if ($ok) {
$self->process_request($c); # This is where the core functionality of a Net::Server should be.
} else {
$self->request_denied_hook($c); # user customizable hook
}
$self->post_process_request_hook($ok); # user customizable hook
$self->post_process_request; # clean up client connection, etc
$self->post_client_connection_hook; # one last hook
}
###----------------------------------------------------------------###
sub _initialize {
my $self = shift;
my $prop = $self->{'server'} ||= {};
$self->commandline($self->_get_commandline) if ! eval { $self->commandline }; # save for a HUP
$self->configure_hook; # user customizable hook
$self->configure; # allow for reading of commandline, program, and configuration file parameters
my @defaults = %{ $self->default_values || {} }; # allow yet another way to pass defaults
$self->process_args(\@defaults) if @defaults;
}
sub commandline {
my $self = shift;
$self->{'server'}->{'commandline'} = ref($_[0]) ? shift : \@_ if @_;
return $self->{'server'}->{'commandline'} || die "commandline was not set during initialization";
}
sub _get_commandline {
my $self = shift;
my $script = $0;
$script = $ENV{'PWD'} .'/'. $script if $script =~ m|^[^/]+/| && $ENV{'PWD'}; # add absolute to relative - avoid Cwd
$script =~ /^(.+)$/; # untaint for later use in hup
return [$1, @ARGV]
}
sub configure_hook {}
sub configure {
my $self = shift;
my $prop = $self->{'server'};
my $template = ($_[0] && ref($_[0])) ? shift : undef;
$self->process_args(\@ARGV, $template) if @ARGV; # command line
$self->process_args($prop->{'_run_args'}, $template) if $prop->{'_run_args'}; # passed to run
if ($prop->{'conf_file'}) {
$self->process_args($self->_read_conf($prop->{'conf_file'}), $template);
} else {
my $def = $self->default_values || {};
$self->process_args($self->_read_conf($def->{'conf_file'}), $template) if $def->{'conf_file'};
}
}
sub default_values { {} }
sub post_configure {
my $self = shift;
my $prop = $self->{'server'};
$prop->{'log_level'} = 2 if ! defined($prop->{'log_level'}) || $prop->{'log_level'} !~ /^\d+$/;
$prop->{'log_level'} = 4 if $prop->{'log_level'} > 4;
$self->initialize_logging;
if ($prop->{'pid_file'}) { # see if a daemon is already running
if (! eval{ check_pid_file($prop->{'pid_file'}) }) {
warn $@ if !$ENV{'BOUND_SOCKETS'};
$self->fatal(my $e = $@);
}
}
if (! $prop->{'_is_inet'}) { # completetly daemonize by closing STDIN, STDOUT (should be done before fork)
if ($prop->{'setsid'} || length($prop->{'log_file'})) {
open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
}
}
if (!$ENV{'BOUND_SOCKETS'}) { # don't need to redo this if hup'ing
if ($prop->{'setsid'} || $prop->{'background'}) {
my $pid = eval { safe_fork() };
$self->fatal(my $e = $@) if ! defined $pid;
exit(0) if $pid;
$self->log(2, "Process Backgrounded");
}
POSIX::setsid() if $prop->{'setsid'}; # completely remove myself from parent process
}
if (length($prop->{'log_file'})
&& !$prop->{'log_function'}) {
open STDERR, '>&_SERVER_LOG' || die "Cannot open STDERR to _SERVER_LOG [$!]";
} elsif ($prop->{'setsid'}) { # completely daemonize by closing STDERR (should be done after fork)
open STDERR, '>&STDOUT' || die "Cannot open STDERR to STDOUT [$!]";
}
# allow for a pid file (must be done after backgrounding and chrooting)
# Remove of this pid may fail after a chroot to another location... however it doesn't interfere either.
if ($prop->{'pid_file'}) {
if (eval { create_pid_file($prop->{'pid_file'}) }) {
$prop->{'pid_file_unlink'} = 1;
} else {
$self->fatal(my $e = $@);
}
}
# make sure that allow and deny look like array refs
$prop->{$_} = [] for grep {! ref $prop->{$_}} qw(allow deny cidr_allow cidr_deny);
}
sub initialize_logging {
my $self = shift;
my $prop = $self->{'server'};
if (! defined($prop->{'log_file'})) {
$prop->{'log_file'} = ''; # log to STDERR
return;
}
# pluggable logging
if ($prop->{'log_file'} =~ /^([a-zA-Z]\w*(?:::[a-zA-Z]\w*)*)$/) {
my $pkg = "Net::Server::Log::$prop->{'log_file'}";
(my $file = "$pkg.pm") =~ s|::|/|g;
if (eval { require $file }) {
$prop->{'log_function'} = $pkg->initialize($self);
$prop->{'log_class'} = $pkg;
return;
} elsif ($file =~ /::/ || grep {-e "$_/$file"} @INC) {
$self->fatal("Unable to load log module $pkg from file $file: $@");
}
}
# regular file based logging
die "Unsecure filename \"$prop->{'log_file'}\"" if $prop->{'log_file'} !~ m|^([\:\w\.\-/\\]+)$|;
$prop->{'log_file'} = $1; # open a logging file
open(_SERVER_LOG, ">>", $prop->{'log_file'})
|| die "Couldn't open log file \"$prop->{'log_file'}\" [$!].";
_SERVER_LOG->autoflush(1);
push @{ $prop->{'chown_files'} }, $prop->{'log_file'};
}
sub post_configure_hook {}
sub _server_type { ref($_[0]) }
sub pre_bind { # make sure we have good port parameters
my $self = shift;
my $prop = $self->{'server'};
my $super = $self->net_server_type;
my $type = $self->_server_type;
if ($self->isa('Net::Server::MultiType')) {
my $base = delete($prop->{'_recursive_multitype'}) || Net::Server::MultiType->net_server_type;
$super = "$super -> MultiType -> $base";
}
$type .= " (type $super)" if $type ne $super;
$self->log(2, $self->log_time ." $type starting! pid($$)");
$prop->{'sock'} = [grep {$_} map { $self->proto_object($_) } @{ $self->prepared_ports }];
$self->fatal("No valid socket parameters found") if ! @{ $prop->{'sock'} };
}
sub prepared_ports {
my $self = shift;
my $prop = $self->{'server'};
my ($ports, $hosts, $protos, $ipvs) = @$prop{qw(port host proto ipv)};
$ports ||= $prop->{'ports'};
if (!defined($ports) || (ref($ports) && !@$ports)) {
$ports = $self->default_port;
if (!defined($ports) || (ref($ports) && !@$ports)) {
$ports = default_port();
$self->log(2, "Port Not Defined. Defaulting to '$ports'");
}
}
my %bound;
my $bind = $prop->{'_bind'} = [];
for my $_port (ref($ports) ? @$ports : $ports) {
my $_host = ref($hosts) ? $hosts->[ @$bind >= @$hosts ? -1 : $#$bind + 1] : $hosts; # if ports are greater than hosts - augment with the last host
my $_proto = ref($protos) ? $protos->[@$bind >= @$protos ? -1 : $#$bind + 1] : $protos;
my $_ipv = ref($ipvs) ? $ipvs->[ @$bind >= @$ipvs ? -1 : $#$bind + 1] : $ipvs;
foreach my $info ($self->port_info($_port, $_host, $_proto, $_ipv)) {
my ($port, $host, $proto, $ipv) = @$info{qw(port host proto ipv)}; # use cleaned values
if ($port ne "0" && $bound{"$host\e$port\e$proto\e$ipv"}++) {
$self->log(2, "Duplicate configuration (\U$proto\E) on [$host]:$port with IPv$ipv) - skipping");
next;
}
push @$bind, $info;
}
}
return $bind;
}
sub port_info {
my ($self, $port, $host, $proto, $ipv) = @_;
return Net::Server::Proto->parse_info($port, $host, $proto, $ipv, $self);
}
sub proto_object {
my ($self, $info) = @_;
return Net::Server::Proto->object($info, $self);
}
sub bind { # bind to the port (This should serve all but INET)
my $self = shift;
my $prop = $self->{'server'};
if (exists $ENV{'BOUND_SOCKETS'}) {
$self->restart_open_hook;
$self->log(2, "Binding open file descriptors");
my %map;
foreach my $info (split /\s*;\s*/, $ENV{'BOUND_SOCKETS'}) {
my ($fd, $host, $port, $proto, $ipv, $orig) = split /\|/, $info;
$orig = $port if ! defined $orig; # allow for things like service ports or port 0
$fd = ($fd =~ /^(\d+)$/) ? $1 : $self->fatal("Bad file descriptor");
$map{"$host|$orig|$proto|$ipv"}->{$fd} = $port;
}
foreach my $sock (@{ $prop->{'sock'} }) {
$sock->log_connect($self);
if (my $ref = $map{$sock->hup_string}) {
my ($fd, $port) = each %$ref;
$sock->reconnect($fd, $self, $port);
delete $ref->{$fd};
delete $map{$sock->hup_string} if ! keys %$ref;
} else {
$self->log(2, "Added new port configuration");
$sock->connect($self);
}
}
foreach my $str (keys %map) {
foreach my $fd (keys %{ $map{$str} }) {
$self->log(2, "Closing un-mapped port ($str) on fd $fd");
POSIX::close($fd);
}
}
delete $ENV{'BOUND_SOCKETS'};
$self->{'hup_waitpid'} = 1;
} else { # connect to fresh ports
foreach my $sock (@{ $prop->{'sock'} }) {
$sock->log_connect($self);
$sock->connect($self);
}
}
if (@{ $prop->{'sock'} } > 1 || $prop->{'multi_port'}) {
$prop->{'multi_port'} = 1;
$prop->{'select'} = IO::Select->new; # if more than one socket we'll need to select on it
$prop->{'select'}->add($_) for @{ $prop->{'sock'} };
} else {
$prop->{'multi_port'} = undef;
$prop->{'select'} = undef;
}
}
sub post_bind_hook {}
sub post_bind { # secure the process and background it
my $self = shift;
my $prop = $self->{'server'};
if (! defined $prop->{'group'}) {
$self->log(1, "Group Not Defined. Defaulting to EGID '$)'");
$prop->{'group'} = $);
} elsif ($prop->{'group'} =~ /^([\w-]+(?: [\w-]+)*)$/) {
$prop->{'group'} = eval { get_gid($1) };
$self->fatal(my $e = $@) if $@;
} else {
$self->fatal("Invalid group \"$prop->{'group'}\"");
}
if (! defined $prop->{'user'}) {
$self->log(1, "User Not Defined. Defaulting to EUID '$>'");
$prop->{'user'} = $>;
} elsif ($prop->{'user'} =~ /^([\w-]+)$/) {
$prop->{'user'} = eval { get_uid($1) };
$self->fatal(my $e = $@) if $@;
} else {
$self->fatal("Invalid user \"$prop->{'user'}\"");
}
# chown any files or sockets that we need to
if ($prop->{'group'} ne $) || $prop->{'user'} ne $>) {
my @chown_files;
push @chown_files, map {$_->NS_port} grep {$_->NS_proto =~ /^UNIX/} @{ $prop->{'sock'} };
push @chown_files, $prop->{'pid_file'} if $prop->{'pid_file_unlink'};
push @chown_files, $prop->{'lock_file'} if $prop->{'lock_file_unlink'};
push @chown_files, @{ $prop->{'chown_files'} || [] };
my $uid = $prop->{'user'};
my $gid = (split /\ /, $prop->{'group'})[0];
foreach my $file (@chown_files){
chown($uid, $gid, $file) || $self->fatal("Couldn't chown \"$file\" [$!]");
}
}
if ($prop->{'chroot'}) {
$self->fatal("Specified chroot \"$prop->{'chroot'}\" doesn't exist.") if ! -d $prop->{'chroot'};
$self->log(2, "Chrooting to $prop->{'chroot'}");
chroot($prop->{'chroot'}) || $self->fatal("Couldn't chroot to \"$prop->{'chroot'}\": $!");
}
# drop privileges
eval {
if ($prop->{'group'} ne $)) {
$self->log(2, "Setting gid to \"$prop->{'group'}\"");
set_gid($prop->{'group'} );
}
if ($prop->{'user'} ne $>) {
$self->log(2, "Setting uid to \"$prop->{'user'}\"");
set_uid($prop->{'user'});
}
};
if ($@) {
if ($> == 0) {
$self->fatal(my $e = $@);
} elsif ($< == 0) {
$self->log(2, "NOTICE: Effective UID changed, but Real UID is 0: $@");
} else {
$self->log(2, my $e = $@);
}
}
$prop->{'requests'} = 0; # record number of request
$SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->server_close; };
$SIG{'PIPE'} = 'IGNORE'; # most cases, a closed pipe will take care of itself
$SIG{'CHLD'} = \&sig_chld; # catch children (mainly for Fork and PreFork but works for any chld)
$SIG{'HUP'} = sub { $self->sig_hup };
}
sub sig_chld {
1 while waitpid(-1, POSIX::WNOHANG()) > 0;
$SIG{'CHLD'} = \&sig_chld;
}
sub pre_loop_hook {}
sub loop {
my $self = shift;
while ($self->accept) {
$self->run_client_connection;
last if $self->done;
}
}
sub accept {
my $self = shift;
my $prop = $self->{'server'};
my $sock = undef;
my $retries = 30;
while ($retries--) {
if ($prop->{'multi_port'}) { # with more than one port, use select to get the next one
return 0 if $prop->{'_HUP'};
$sock = $self->accept_multi_port || next; # keep trying for the rest of retries
return 0 if $prop->{'_HUP'};
if ($self->can_read_hook($sock)) {
$retries++;
next;
}
} else {
$sock = $prop->{'sock'}->[0]; # single port is bound - just accept
}
$self->fatal("Received a bad sock!") if ! defined $sock;
if (SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(), Socket::SO_TYPE())) { # receive a udp packet
$prop->{'client'} = $sock;
$prop->{'udp_true'} = 1;
$prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags);
} else { # blocking accept per proto
delete $prop->{'udp_true'};
$prop->{'client'} = $sock->accept();
}
return 0 if $prop->{'_HUP'};
return 1 if $prop->{'client'};
$self->log(2,"Accept failed with $retries tries left: $!");
sleep(1);
}
$self->log(1,"Ran out of accept retries!");
return undef;
}
sub accept_multi_port {
my @waiting = shift->{'server'}->{'select'}->can_read();
return undef if ! @waiting;
return $waiting[rand @waiting];
}
sub can_read_hook {}
sub post_accept {
my $self = shift;
my $prop = $self->{'server'};
my $client = shift || $prop->{'client'};
$prop->{'requests'}++;
return if $prop->{'udp_true'}; # no need to do STDIN/STDOUT in UDP
if (!$client) {
$self->log(1,"Client socket information could not be determined!");
return;
}
$client->post_accept() if $client->can("post_accept");
if (! $prop->{'no_client_stdout'}) {
close STDIN; # duplicate some handles and flush them
close STDOUT;
if ($prop->{'tie_client_stdout'} || ($client->can('tie_stdout') && $client->tie_stdout)) {
open STDIN, '<', '/dev/null' or die "Couldn't open STDIN to the client socket: $!";
open STDOUT, '>', '/dev/null' or die "Couldn't open STDOUT to the client socket: $!";
tie *STDOUT, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdout_callback'} or die "Couldn't tie STDOUT: $!";
tie *STDIN, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdin_callback'} or die "Couldn't tie STDIN: $!";
} elsif (defined(my $fileno = fileno $prop->{'client'})) {
open STDIN, '<&', $fileno or die "Couldn't open STDIN to the client socket: $!";
open STDOUT, '>&', $fileno or die "Couldn't open STDOUT to the client socket: $!";
} else {
*STDIN = \*{ $client };
*STDOUT = \*{ $client };
}
STDIN->autoflush(1);
STDOUT->autoflush(1);
select STDOUT;
}
}
sub get_client_info {
my $self = shift;
my $prop = $self->{'server'};
my $client = shift || $prop->{'client'};
if ($client->NS_proto =~ /^UNIX/) {
delete @$prop{qw(sockaddr sockport peeraddr peerport peerhost)};
$self->log(3, $self->log_time." CONNECT ".$client->NS_proto." Socket: \"".$client->NS_port."\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
return;
}
if (my $sockname = $client->sockname) {
$prop->{'sockaddr'} = $client->sockhost;
$prop->{'sockport'} = $client->sockport;
} else {
@{ $prop }{qw(sockaddr sockhost sockport)} = ($ENV{'REMOTE_HOST'} || '0.0.0.0', 'inet.test', 0); # commandline
}
my $addr;
if ($prop->{'udp_true'}) {
if ($client->sockdomain == AF_INET) {
($prop->{'peerport'}, $addr) = Socket::sockaddr_in($prop->{'udp_peer'});
$prop->{'peeraddr'} = Socket::inet_ntoa($addr);
} else {
warn "Right here\n";
($prop->{'peerport'}, $addr) = Socket6::sockaddr_in6($prop->{'udp_peer'});
$prop->{'peeraddr'} = Socket6->can('inet_ntop')
? Socket6::inet_ntop($client->sockdomain, $addr)
: Socket::inet_ntoa($addr);
}
} elsif ($prop->{'peername'} = $client->peername) {
$addr = $client->peeraddr;
$prop->{'peeraddr'} = $client->peerhost;
$prop->{'peerport'} = $client->peerport;
} else {
@{ $prop }{qw(peeraddr peerhost peerport)} = ('0.0.0.0', 'inet.test', 0); # commandline
}
if ($addr && defined $prop->{'reverse_lookups'}) {
if ($INC{'Socket6.pm'} && Socket6->can('getnameinfo')) {
my @res = Socket6::getnameinfo($addr, 0);
$prop->{'peerhost'} = $res[0] if @res > 1;
}else{
$prop->{'peerhost'} = gethostbyaddr($addr, AF_INET);
}
}
$self->log(3, $self->log_time
." CONNECT ".$client->NS_proto
." Peer: \"[$prop->{'peeraddr'}]:$prop->{'peerport'}\""
." Local: \"[$prop->{'sockaddr'}]:$prop->{'sockport'}\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
}
sub post_accept_hook {}
sub allow_deny {
my $self = shift;
my $prop = $self->{'server'};
my $sock = shift || $prop->{'client'};
# unix sockets are immune to this check
return 1 if $sock && $sock->NS_proto =~ /^UNIX/;
# if no allow or deny parameters are set, allow all
return 1 if ! @{ $prop->{'allow'} }
&& ! @{ $prop->{'deny'} }
&& ! @{ $prop->{'cidr_allow'} }
&& ! @{ $prop->{'cidr_deny'} };
# work around Net::CIDR::cidrlookup() croaking,
# if first parameter is an IPv4 address in IPv6 notation.
my $peeraddr = ($prop->{'peeraddr'} =~ /^\s*::ffff:([0-9.]+\s*)$/) ? $1 : $prop->{'peeraddr'};
# if the addr or host matches a deny, reject it immediately
foreach (@{ $prop->{'deny'} }) {
return 0 if $prop->{'reverse_lookups'}
&& defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
return 0 if $peeraddr =~ /^$_$/;
}
if (@{ $prop->{'cidr_deny'} }) {
require Net::CIDR;
return 0 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_deny'} });
}
# if the addr or host isn't blocked yet, allow it if it is allowed
foreach (@{ $prop->{'allow'} }) {
return 1 if $prop->{'reverse_lookups'}
&& defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
return 1 if $peeraddr =~ /^$_$/;
}
if (@{ $prop->{'cidr_allow'} }) {
require Net::CIDR;
return 1 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_allow'} });
}
return 0;
}
sub allow_deny_hook { 1 } # false to deny request
sub request_denied_hook {}
sub process_request { # sample echo server - override for full functionality
my $self = shift;
my $prop = $self->{'server'};
if ($prop->{'udp_true'}) { # udp echo server
my $client = shift || $prop->{'client'};
if ($prop->{'udp_data'} =~ /dump/) {
require Data::Dumper;
return $client->send(Data::Dumper::Dumper($self), 0);
}
return $client->send("You said \"$prop->{'udp_data'}\"", 0);
}
print "Welcome to \"".ref($self)."\" ($$)\015\012";
my $previous_alarm = alarm 30;
eval {
local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
while () {
s/[\r\n]+$//;
print ref($self),":$$: You said \"$_\"\015\012";
$self->log(5, $_); # very verbose log
if (/get\s+(\w+)/) { print "$1: $self->{'server'}->{$1}\015\012" }
elsif (/dump/) { require Data::Dumper; print Data::Dumper::Dumper($self) }
elsif (/quit/) { last }
elsif (/exit/) { $self->server_close }
alarm 30; # another 30
}
alarm($previous_alarm);
};
alarm 0;
print "Timed Out.\015\012" if $@ eq "Timed Out!\n";
}
sub post_process_request_hook {}
sub post_client_connection_hook {}
sub post_process_request {
my $self = shift;
$self->close_client_stdout;
}
sub close_client_stdout {
my $self = shift;
my $prop = $self->{'server'};
return if $prop->{'udp_true'};
if (! $prop->{'no_client_stdout'}) {
my $t = tied *STDOUT; if ($t) { undef $t; untie *STDOUT };
$t = tied *STDIN; if ($t) { undef $t; untie *STDIN };
open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
}
$prop->{'client'}->close;
}
sub done {
my $self = shift;
$self->{'server'}->{'done'} = shift if @_;
return $self->{'server'}->{'done'};
}
sub pre_fork_hook {}
sub child_init_hook {}
sub child_finish_hook {}
sub run_dequeue { # fork off a child process to handle dequeuing
my $self = shift;
$self->pre_fork_hook('dequeue');
my $pid = fork;
$self->fatal("Bad fork [$!]") if ! defined $pid;
if (!$pid) { # child
$SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = $SIG{'HUP'} = sub {
$self->child_finish_hook('dequeue');
exit;
};
$SIG{'PIPE'} = $SIG{'TTIN'} = $SIG{'TTOU'} = 'DEFAULT';
$self->child_init_hook('dequeue');
$self->dequeue();
$self->child_finish_hook('dequeue');
exit;
}
$self->log(4, "Running dequeue child $pid");
$self->{'server'}->{'children'}->{$pid}->{'status'} = 'dequeue'
if $self->{'server'}->{'children'};
}
sub default_port { 20203 }
sub dequeue {}
sub pre_server_close_hook {}
sub server_close {
my ($self, $exit_val) = @_;
my $prop = $self->{'server'};
$SIG{'INT'} = 'DEFAULT';
### if this is a child process, signal the parent and close
### normally the child shouldn't, but if they do...
### otherwise the parent continues with the shutdown
### this is safe for non standard forked child processes
### as they will not have server_close as a handler
if (defined($prop->{'ppid'})
&& $prop->{'ppid'} != $$
&& ! defined($prop->{'no_close_by_child'})) {
$self->close_parent;
exit;
}
$self->pre_server_close_hook;
$self->log(2, $self->log_time . " Server closing!");
if ($prop->{'kind_quit'} && $prop->{'children'}) {
$self->log(3, "Attempting a slow shutdown");
$prop->{$_} = 0 for qw(min_servers max_servers);
$self->hup_children; # send children signal to finish up
while (1) {
Net::Server::SIG::check_sigs();
$self->coordinate_children if $self->can('coordinate_children');
last if !keys %{$self->{'server'}->{'children'}};
sleep 1;
}
}
if ($prop->{'_HUP'} && $prop->{'leave_children_open_on_hup'}) {
$self->hup_children;
} else {
$self->close_children() if $prop->{'children'};
$self->post_child_cleanup_hook;
}
if (defined($prop->{'lock_file'})
&& -e $prop->{'lock_file'}
&& defined($prop->{'lock_file_unlink'})) {
unlink($prop->{'lock_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'lock_file'}\" [$!]");
}
if (defined($prop->{'pid_file'})
&& -e $prop->{'pid_file'}
&& !$prop->{'_HUP'}
&& defined($prop->{'pid_file_unlink'})) {
unlink($prop->{'pid_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'pid_file'}\" [$!]");
}
if ($prop->{'_HUP'}) {
$self->restart_close_hook();
$self->hup_server; # execs at the end
}
$self->shutdown_sockets;
return $self if $prop->{'no_exit_on_close'};
$self->server_exit($exit_val);
}
sub server_exit {
my ($self, $exit_val) = @_;
exit($exit_val || 0);
}
sub shutdown_sockets {
my $self = shift;
my $prop = $self->{'server'};
foreach my $sock (@{ $prop->{'sock'} }) { # unlink remaining socket files (if any)
$sock->shutdown(2);
unlink $sock->NS_port if $sock->NS_proto =~ /^UNIX/;
}
$prop->{'sock'} = []; # delete the sock objects
return 1;
}
### Allow children to send INT signal to parent (or use another method)
### This method is only used by forking servers
sub close_parent {
my $self = shift;
my $prop = $self->{'server'};
die "Missing parent pid (ppid)" if ! $prop->{'ppid'};
kill 2, $prop->{'ppid'};
}
### SIG INT the children
### This method is only used by forking servers (ie Fork, PreFork)
sub close_children {
my $self = shift;
my $prop = $self->{'server'};
return unless $prop->{'children'} && scalar keys %{ $prop->{'children'} };
foreach my $pid (keys %{ $prop->{'children'} }) {
$self->log(4, "Kill TERM pid $pid");
if (kill(15, $pid) || ! kill(0, $pid)) { # if it is killable, kill it
$self->delete_child($pid);
}
}
1 while waitpid(-1, POSIX::WNOHANG()) > 0;
}
sub is_prefork { 0 }
sub hup_children {
my $self = shift;
my $prop = $self->{'server'};
return unless defined $prop->{'children'} && scalar keys %{ $prop->{'children'} };
return if ! $self->is_prefork;
$self->log(2, "Sending children hup signal");
for my $pid (keys %{ $prop->{'children'} }) {
$self->log(4, "Kill HUP pid $pid");
kill(1, $pid) or $self->log(2, "Failed to kill pid $pid: $!");
}
}
sub post_child_cleanup_hook {}
### handle sig hup
### this will prepare the server for a restart via exec
sub sig_hup {
my $self = shift;
my $prop = $self->{'server'};
$self->log(2, "Received a SIG HUP");
my $i = 0;
my @fd;
$prop->{'_HUP'} = [];
foreach my $sock (@{ $prop->{'sock'} }) {
my $fd = POSIX::dup($sock->fileno) || $self->fatal("Cannot duplicate the socket [$!]");
# hold on to the socket copy until exec;
# just temporary: any socket domain will do,
# forked process will decide to use IO::Socket::INET6 if necessary
$prop->{'_HUP'}->[$i] = IO::Socket::INET->new;
$prop->{'_HUP'}->[$i]->fdopen($fd, 'w') || $self->fatal("Cannot open to file descriptor [$!]");
# turn off the FD_CLOEXEC bit to allow reuse on exec
require Fcntl;
$prop->{'_HUP'}->[$i]->fcntl(Fcntl::F_SETFD(), my $flags = "");
push @fd, $fd .'|'. $sock->hup_string; # save file-descriptor and host|port|proto|ipv
$sock->close();
$i++;
}
delete $prop->{'select'}; # remove any blocking obstacle
$ENV{'BOUND_SOCKETS'} = join "; ", @fd;
if ($prop->{'leave_children_open_on_hup'} && scalar keys %{ $prop->{'children'} }) {
$ENV{'HUP_CHILDREN'} = join "\n", map {"$_\t$prop->{'children'}->{$_}->{'status'}"} sort keys %{ $prop->{'children'} };
}
}
sub hup_server {
my $self = shift;
$self->log(0, $self->log_time()." Re-exec server during HUP");
delete @ENV{$self->hup_delete_env_keys};
exec @{ $self->commandline };
}
sub hup_delete_env_keys { return qw(PATH) }
sub restart_open_hook {} # this hook occurs if a server has been HUP'ed it occurs just before opening to the fileno's
sub restart_close_hook {} # this hook occurs if a server has been HUP'ed it occurs just before exec'ing the server
###----------------------------------------------------------###
sub fatal {
my ($self, $error) = @_;
my ($package, $file, $line) = caller;
$self->fatal_hook($error, $package, $file, $line);
$self->log(0, $self->log_time ." $error\n at line $line in file $file");
$self->server_close(1);
}
sub fatal_hook {}
###----------------------------------------------------------###
sub log {
my ($self, $level, $msg, @therest) = @_;
my $prop = $self->{'server'};
return if ! $prop->{'log_level'};
return if $level =~ /^\d+$/ && $level > $prop->{'log_level'};
$msg = sprintf($msg, @therest) if @therest; # if multiple arguments are passed, assume that the first is a format string
if ($prop->{'log_function'}) {
return if eval { $prop->{'log_function'}->($level, $msg); 1 };
my $err = $@;
if ($prop->{'log_class'} && $prop->{'log_class'}->can('handle_error')) {
$prop->{'log_class'}->handle_log_error($self, $err, [$level, $msg]);
} else {
$self->handle_log_error($err, [$level, $msg]);
}
}
return if $level !~ /^\d+$/;
$self->write_to_log_hook($level, $msg);
}
sub handle_log_error { my ($self, $error) = @_; die $error }
sub handle_syslog_error { &handle_log_error }
sub write_to_log_hook {
my ($self, $level, $msg) = @_;
my $prop = $self->{'server'};
chomp $msg;
$msg =~ s/([^\n\ -\~])/sprintf("%%%02X",ord($1))/eg;
if ($prop->{'log_file'}) {
print _SERVER_LOG $msg, "\n";
} elsif ($prop->{'setsid'}) {
# do nothing ?
} else {
my $old = select STDERR;
print $msg. "\n";
select $old;
}
}
sub log_time {
my ($sec,$min,$hour,$day,$mon,$year) = localtime;
return sprintf "%04d/%02d/%02d-%02d:%02d:%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec;
}
###----------------------------------------------------------###
sub options {
my $self = shift;
my $ref = shift || {};
my $prop = $self->{'server'};
foreach (qw(port host proto ipv allow deny cidr_allow cidr_deny)) {
if (! defined $prop->{$_}) {
$prop->{$_} = [];
} elsif (! ref $prop->{$_}) {
$prop->{$_} = [$prop->{$_}]; # nicely turn us into an arrayref if we aren't one already
}
$ref->{$_} = $prop->{$_};
}
foreach (qw(conf_file
user group chroot log_level
log_file pid_file background setsid
listen reverse_lookups
no_close_by_child
no_client_stdout tie_client_stdout tied_stdout_callback tied_stdin_callback
leave_children_open_on_hup
)) {
$ref->{$_} = \$prop->{$_};
}
return $ref;
}
### routine for parsing commandline, module, and conf file
### method has the benefit of leaving unused arguments in @ARGV
sub process_args {
my ($self, $args, $template) = @_;
$self->options($template = {}) if ! $template || ! ref $template;
if (!$_[2] && !scalar(keys %$template) && !$self->{'server'}->{'_no_options'}++) {
warn "Configuration options were empty - skipping any commandline, config file, or run argument parsing.\n";
}
# we want subsequent calls to not overwrite or add to previously set values so that command line arguments win
my %previously_set;
foreach (my $i = 0; $i < @$args; $i++) {
if ($args->[$i] =~ /^(?:--)?(\w+)(?:[=\ ](\S+))?$/
&& exists $template->{$1}) {
my ($key, $val) = ($1, $2);
splice @$args, $i, 1;
if (! defined $val) {
if ($i > $#$args
|| ($args->[$i] && $args->[$i] =~ /^--\w+/)) {
$val = 1; # allow for options such as --setsid
} else {
$val = splice @$args, $i, 1;
$val = $val->[0] if ref($val) eq 'ARRAY' && @$val == 1 && ref($template->{$key}) ne 'ARRAY';
}
}
$i--;
$val =~ s/%([A-F0-9])/chr(hex $1)/eig if ! ref $val;
if (ref $template->{$key} eq 'ARRAY') {
if (! defined $previously_set{$key}) {
$previously_set{$key} = scalar @{ $template->{$key} };
}
next if $previously_set{$key};
push @{ $template->{$key} }, ref($val) eq 'ARRAY' ? @$val : $val;
} else {
if (! defined $previously_set{$key}) {
$previously_set{$key} = defined(${ $template->{$key} }) ? 1 : 0;
}
next if $previously_set{$key};
die "Found multiple values on the configuration item \"$key\" which expects only one value" if ref($val) eq 'ARRAY';
${ $template->{$key} } = $val;
}
}
}
}
sub _read_conf {
my ($self, $file) = @_;
my @args;
$file = ($file =~ m|^([\w\.\-\/\\\:]+)$|) ? $1 : $self->fatal("Unsecure filename \"$file\"");
open my $fh, '<', $file or do {
$self->fatal("Couldn't open conf \"$file\" [$!]") if $ENV{'BOUND_SOCKETS'};
warn "Couldn't open conf \"$file\" [$!]\n";
};
while (defined(my $line = <$fh>)) {
push @args, $1, $2 if $line =~ m/^\s* ((?:--)?\w+) (?:\s*[=:]\s*|\s+) (\S+)/x;
}
close $fh;
return \@args;
}
###----------------------------------------------------------------###
sub other_child_died_hook {}
sub delete_child {
my ($self, $pid) = @_;
my $prop = $self->{'server'};
return $self->other_child_died_hook($pid) if ! exists $prop->{'children'}->{$pid};
# prefork server check to clear child communication
if ($prop->{'child_communication'}) {
if ($prop->{'children'}->{$pid}->{'sock'}) {
$prop->{'child_select'}->remove($prop->{'children'}->{$pid}->{'sock'});
$prop->{'children'}->{$pid}->{'sock'}->close;
}
}
delete $prop->{'children'}->{$pid};
}
# send signal to all children - used by forking servers
sub sig_pass {
my ($self, $sig) = @_;
foreach my $chld (keys %{ $self->{'server'}->{'children'} }) {
$self->log(4, "signaling $chld with $sig" );
kill($sig, $chld) || $self->log(1, "child $chld not signaled with $sig");
}
}
# register sigs to allow passthrough to children
sub register_sig_pass {
my $self = shift;
my $ref = $self->{'server'}->{'sig_passthrough'} || [];
$ref = [$ref] if ! ref $ref;
$self->fatal('invalid sig_passthrough') if ref $ref ne 'ARRAY';
return if ! @$ref;
$self->log(4, "sig_passthrough option found");
require Net::Server::SIG;
foreach my $sig (map {split /\s*,\s*/, $_} @$ref) {
my $code = Net::Server::SIG::sig_is_registered($sig);
if ($code) {
$self->log(2, "Installing passthrough for $sig even though it is already registered.");
} else {
$code = ref($SIG{$sig}) eq 'CODE' ? $SIG{$sig} : undef;
}
Net::Server::SIG::register_sig($sig => sub { $self->sig_pass($sig); $code->($sig) if $code; });
$self->log(2, "Installed passthrough for $sig");
}
}
###----------------------------------------------------------------###
package Net::Server::TiedHandle;
sub TIEHANDLE { my $pkg = shift; return bless [@_], $pkg }
sub READLINE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'getline', @_) : $s->[0]->getline }
sub SAY { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'say', @_) : $s->[0]->say(@_) }
sub PRINT { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'print', @_) : $s->[0]->print(@_) }
sub PRINTF { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'printf', @_) : $s->[0]->printf(@_) }
sub READ { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'read', @_) : $s->[0]->read(@_) }
sub WRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'write', @_) : $s->[0]->write(@_) }
sub SYSREAD { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'sysread', @_) : $s->[0]->sysread(@_) }
sub SYSWRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'syswrite', @_) : $s->[0]->syswrite(@_) }
sub SEEK { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'seek', @_) : $s->[0]->seek(@_) }
sub BINMODE {}
sub FILENO {}
sub CLOSE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'close', @_) : $s->[0]->close(@_) }
1;
### The documentation is in Net/Server.pod
Net-Server-2.008/lib/Net/Server/ 0000755 0001750 0001750 00000000000 12334210320 014731 5 ustar paul paul Net-Server-2.008/lib/Net/Server/Daemonize.pm 0000644 0001750 0001750 00000022743 12331755703 017232 0 ustar paul paul # -*- perl -*-
#
# Net::Server::Daemonize - Daemonization utilities.
#
# $Id$
#
# Copyright (C) 2001-2012
#
# Jeremy Howard
# j+daemonize@howard.fm
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
# All rights reserved.
#
################################################################
package Net::Server::Daemonize;
use strict;
use base qw(Exporter);
use POSIX qw(SIGINT SIG_BLOCK SIG_UNBLOCK);
our $VERSION = "0.06";
our @EXPORT_OK = qw(check_pid_file create_pid_file unlink_pid_file
is_root_user get_uid get_gid set_uid set_gid
set_user safe_fork daemonize);
###----------------------------------------------------------------###
### check for existance of pid_file
### if the file exists, check for a running process
sub check_pid_file ($) {
my $pid_file = shift;
return 1 if ! -e $pid_file;
open my $fh, '<', $pid_file or die "Couldn't open existant pid_file \"$pid_file\" [$!]\n";
my $current_pid = <$fh>;
close $fh;
$current_pid = ($current_pid =~ /^(\d{1,10})/) ? $1 : die "Couldn't find pid in existing pid_file";
my $exists;
if ($$ == $current_pid) {
warn "Pid_file created by this same process. Doing nothing.\n";
return 1;
} elsif (-d "/proc/$$") { # try a proc file system
$exists = -e "/proc/$current_pid";
} elsif (kill 0, $current_pid) {
$exists = 1;
}
die "Pid_file already exists for running process ($current_pid)... aborting\n"
if $exists;
# remove the pid_file
warn "Pid_file \"$pid_file\" already exists. Overwriting!\n";
unlink $pid_file || die "Couldn't remove pid_file \"$pid_file\" [$!]\n";
return 1;
}
### actually create the pid_file, calls check_pid_file
### before proceeding
sub create_pid_file ($) {
my $pid_file = shift;
check_pid_file($pid_file);
open my $fh, '>', $pid_file or die "Couldn't open pid file \"$pid_file\" [$!].\n";
print $fh "$$\n";
close $fh;
die "Pid_file \"$pid_file\" not created.\n" if ! -e $pid_file;
return 1;
}
### Allow for safe removal of the pid_file.
### Make sure this process owns it.
sub unlink_pid_file ($) {
my $pid_file = shift;
return 1 if ! -e $pid_file; # no pid_file = return success
open my $fh, '<', $pid_file or die "Couldn't open existant pid_file \"$pid_file\" [$!]\n"; # slight race
my $current_pid = <$fh>;
close $fh;
chomp $current_pid;
die "Process $$ doesn't own pid_file \"$pid_file\". Can't remove it.\n"
if $current_pid ne $$;
unlink($pid_file) || die "Couldn't unlink pid_file \"$pid_file\" [$!]\n";
return 1;
}
###----------------------------------------------------------------###
sub is_root_user () {
my $id = get_uid('root');
return ! defined($id) || $< == $id || $> == $id;
}
### get the uid for the passed user
sub get_uid ($) {
my $user = shift;
my $uid = ($user =~ /^(\d+)$/) ? $1 : getpwnam($user);
die "No such user \"$user\"\n" unless defined $uid;
return $uid;
}
### get all of the gids that this group is (space delimited)
sub get_gid {
my @gid;
foreach my $group ( split( /[, ]+/, join(" ",@_) ) ){
if( $group =~ /^\d+$/ ){
push @gid, $group;
}else{
my $id = getgrnam($group);
die "No such group \"$group\"\n" unless defined $id;
push @gid, $id;
}
}
die "No group found in arguments.\n" unless @gid;
return join(" ",$gid[0],@gid);
}
### change the process to run as this uid
sub set_uid {
my $uid = get_uid(shift());
POSIX::setuid($uid);
if ($< != $uid || $> != $uid) { # check $> also (rt #21262)
$< = $> = $uid; # try again - needed by some 5.8.0 linux systems (rt #13450)
if ($< != $uid) {
die "Couldn't become uid \"$uid\": $!\n";
}
}
return 1;
}
### change the process to run as this gid(s)
### multiple groups must be space or comma delimited
sub set_gid {
my $gids = get_gid(@_);
my $gid = (split /\s+/, $gids)[0];
eval { $) = $gids }; # store all the gids - this is really sort of optional
POSIX::setgid($gid);
if (! grep {$gid == $_} split /\s+/, $() { # look for any valid id in the list
die "Couldn't become gid \"$gid\": $!\n";
}
return 1;
}
### backward compatibility sub
sub set_user {
my ($user, @group) = @_;
set_gid(@group) || return undef;
set_uid($user) || return undef;
return 1;
}
###----------------------------------------------------------------###
### routine to protect process during fork
sub safe_fork () {
# block signal for fork
my $sigset = POSIX::SigSet->new(SIGINT);
POSIX::sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: [$!]\n";
my $pid = fork;
die "Couldn't fork: [$!]" if ! defined $pid;
$SIG{'INT'} = 'DEFAULT'; # make SIGINT kill us as it did before
POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: [$!]\n";
return $pid;
}
###----------------------------------------------------------------###
### routine to completely dissociate from terminal process.
sub daemonize ($$$) {
my ($user, $group, $pid_file) = @_;
check_pid_file($pid_file) if defined $pid_file;
my $uid = get_uid($user);
my $gid = get_gid($group); # returns list of groups
$gid = (split /\s+/, $gid)[0];
my $pid = safe_fork();
exit(0) if $pid; # exit parent
# child
create_pid_file($pid_file) if defined $pid_file;
chown($uid, $gid, $pid_file) if defined $pid_file;
set_user($uid, $gid);
open STDIN, '<', '/dev/null' or die "Can't open STDIN from /dev/null: [$!]\n";
open STDOUT, '>', '/dev/null' or die "Can't open STDOUT to /dev/null: [$!]\n";
open STDERR, '>&STDOUT' or die "Can't open STDERR to STDOUT: [$!]\n";
### does this mean to be chroot ?
chdir '/' or die "Can't chdir to \"/\": [$!]";
POSIX::setsid(); # Turn process into session leader, and ensure no controlling terminal
### install a signal handler to make sure SIGINT's remove our pid_file
$SIG{'INT'} = sub { HUNTSMAN($pid_file) } if defined $pid_file;
return 1;
}
### SIGINT routine that will remove the pid_file
sub HUNTSMAN {
my $path = shift;
unlink $path;
eval {
require Unix::Syslog;
Unix::Syslog::syslog(Unix::Syslog::LOG_ERR(), "Exiting on INT signal.");
};
exit;
}
1;
__END__
=head1 NAME
Net::Server::Daemonize - Safe fork and daemonization utilities
=head1 SYNOPSIS
use Net::Server::Daemonize qw(daemonize);
daemonize(
'nobody', # User
'nobody', # Group
'/var/state/mydaemon.pid' # Path to PID file - optional
);
=head1 DESCRIPTION
This module is intended to let you simply and safely daemonize your
server on systems supporting the POSIX module. This means that your
Perl script runs in the background, and it's process ID is stored in a
file so you can easily stop it later.
=head1 EXPORTED FUNCTIONS
=over 4
=item daemonize
Main routine. Arguments are user (or userid), group (or group id or
space delimited list of groups), and pid_file (path to file). This
routine will check on the pid file, safely fork, create the pid file
(storing the pid in the file), become another user and group, close
STDIN, STDOUT and STDERR, separate from the process group (become
session leader), and install $SIG{INT} to remove the pid file. In
otherwords - daemonize. All errors result in a die. As of version
0.89 the pid_file is optional.
=item safe_fork
Block SIGINT during fork. No arguments. Returns pid of forked child.
All errors result in a die.
=item set_user
Become another user and group. Arguments are user (or userid) and
group (or group id or space delimited list of groups).
=item set_uid
Become another user. Argument is user (or userid). All errors die.
=item set_gid
Become another group. Arguments are groups (or group ids or space
delimited list of groups or group ids). All errors die.
=item get_uid
Find the uid. Argument is user (userid returns userid). Returns
userid. All errors die.
=item get_gid
Find the gids. Arguments are groups or space delimited list of
groups. All errors die.
=item is_root_user
Determine if the process is running as root. Returns 1 or undef.
=item check_pid_file
Arguments are pid_file (full path to pid_file). Checks for existance
of pid_file. If file exists, open it and determine if the process
that created it is still running. This is done first by checking for
a /proc file system and second using a "ps" command (BSD syntax). (If
neither of these options exist it assumed that the process has ended)
If the process is still running, it aborts. Otherwise, returns true.
All errors die.
=item create_pid_file.
Arguments are pid_file (full path to pid_file). Calls check_pid_file.
If it is successful (no pid_file exists), creates a pid file and
stores $$ in the file.
=item unlink_pid_file
Does just that.
=back
=head1 SEE ALSO
L.
L, The Perl Cookbook Recipe 17.15.
=head1 AUTHORS
Jeremy Howard
Program flow, concepts and initial work.
Paul Seamons
Code rework and componentization.
Ongoing maintainer.
=head1 LICENSE
This package may be distributed under the terms of either the
GNU General Public License
or the
Perl Artistic License
All rights reserved.
=cut
Net-Server-2.008/lib/Net/Server/Fork.pm 0000644 0001750 0001750 00000022327 12331755703 016216 0 ustar paul paul # -*- perl -*-
#
# Net::Server::Fork - Net::Server personality
#
# $Id$
#
# Copyright (C) 2001-2012
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
# All rights reserved.
#
################################################################
package Net::Server::Fork;
use strict;
use base qw(Net::Server);
use Net::Server::SIG qw(register_sig check_sigs);
use Socket qw(SO_TYPE SOL_SOCKET SOCK_DGRAM);
use POSIX qw(WNOHANG);
sub net_server_type { __PACKAGE__ }
sub options {
my $self = shift;
my $ref = $self->SUPER::options(@_);
my $prop = $self->{'server'};
$ref->{$_} = \$prop->{$_} for qw(max_servers max_dequeue check_for_dead check_for_dequeue);
$ref->{'sig_passthrough'} = $prop->{'sig_passthrough'} = [];
return $ref;
}
sub post_configure {
my $self = shift;
my $prop = $self->{'server'};
$self->SUPER::post_configure(@_);
$prop->{'max_servers'} = 256 if ! defined $prop->{'max_servers'};
$prop->{'check_for_dead'} = 60 if ! defined $prop->{'check_for_dead'};
$prop->{'ppid'} = $$;
$prop->{'multi_port'} = 1;
}
sub loop {
my $self = shift;
my $prop = $self->{'server'};
$prop->{'children'} = {};
if ($ENV{'HUP_CHILDREN'}) {
my %children = map {/^(\w+)$/; $1} split(/\s+/, $ENV{'HUP_CHILDREN'});
$children{$_} = {status => $children{$_}, hup => 1} foreach keys %children;
$prop->{'children'} = \%children;
}
# register some of the signals for safe handling
register_sig(
PIPE => 'IGNORE',
INT => sub { $self->server_close() },
TERM => sub { $self->server_close() },
HUP => sub { $self->sig_hup() },
CHLD => sub {
while (defined(my $chld = waitpid(-1, WNOHANG))) {
last if $chld <= 0;
$self->delete_child($chld);
}
},
QUIT => sub { $self->{'server'}->{'kind_quit'} = 1; $self->server_close() },
TTIN => sub { $self->{'server'}->{'max_servers'}++; $self->log(3, "Increasing max server count ($self->{'server'}->{'max_servers'})") },
TTOU => sub { $self->{'server'}->{'max_servers'}--; $self->log(3, "Decreasing max server count ($self->{'server'}->{'max_servers'})") },
);
$self->register_sig_pass;
if ($ENV{'HUP_CHILDREN'}) {
while (defined(my $chld = waitpid(-1, WNOHANG))) {
last unless $chld > 0;
$self->delete_child($chld);
}
}
my ($last_checked_for_dead, $last_checked_for_dequeue) = (time(), time());
while (1) {
### make sure we don't use too many processes
my $n_children = grep { $_->{'status'} !~ /dequeue/ } values %{ $prop->{'children'} };
while ($n_children > $prop->{'max_servers'}){
select(undef, undef, undef, 5); # block for a moment (don't look too often)
check_sigs();
my $time = time();
if ($time - $last_checked_for_dead > $prop->{'check_for_dead'}) {
$last_checked_for_dead = $time;
$self->log(2, "Max number of children reached ($prop->{max_servers}) -- checking for alive.");
foreach (keys %{ $prop->{'children'} }){
kill(0,$_) or $self->delete_child($_);
}
}
$n_children = grep { $_->{'status'} !~ /dequeue/ } values %{ $prop->{'children'} };
}
if ($prop->{'check_for_dequeue'}) {
my $time = time();
if ($time - $last_checked_for_dequeue > $prop->{'check_for_dequeue'}) {
$last_checked_for_dequeue = $time;
if ($prop->{'max_dequeue'}) {
my $n_dequeue = grep { $_->{'status'} =~ /dequeue/ } values %{ $prop->{'children'} };
$self->run_dequeue() if $n_dequeue < $prop->{'max_dequeue'};
}
}
}
$self->pre_accept_hook;
if (! $self->accept()) {
last if $prop->{'_HUP'};
last if $prop->{'done'};
next;
}
$self->pre_fork_hook;
### fork a child so the parent can go back to listening
local $!;
my $pid = fork;
if (! defined $pid) {
$self->log(1, "Bad fork [$!]");
sleep 5;
next;
}
# child
if (! $pid) {
$self->run_client_connection;
exit;
}
# parent
close($prop->{'client'}) if !$prop->{'udp_true'};
$prop->{'children'}->{$pid}->{'status'} = 'processing';
}
}
sub pre_accept_hook {};
sub accept {
my ($self, $class) = @_;
my $prop = $self->{'server'};
# block on trying to get a handle (select created because we specified multi_port)
my @socks = $prop->{'select'}->can_read(2);
if (check_sigs()) {
return undef if $prop->{'_HUP'};
return undef if ! @socks; # don't continue unless we have a connection
}
my $sock = $socks[rand @socks];
return undef if ! defined $sock;
# check if this is UDP
if (SOCK_DGRAM == $sock->getsockopt(SOL_SOCKET,SO_TYPE)) {
$prop->{'udp_true'} = 1;
$prop->{'client'} = $sock;
$prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags);
# Receive a SOCK_STREAM (TCP or UNIX) packet
} else {
delete $prop->{'udp_true'};
$prop->{'client'} = $sock->accept($class) || return;
}
}
sub run_client_connection {
my $self = shift;
### close the main sock, we still have
### the client handle, this will allow us
### to HUP the parent at any time
$_ = undef foreach @{ $self->{'server'}->{'sock'} };
### restore sigs (for the child)
$SIG{'HUP'} = $SIG{'CHLD'} = $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = 'DEFAULT';
$SIG{'PIPE'} = 'IGNORE';
delete $self->{'server'}->{'children'};
$self->child_init_hook;
$self->SUPER::run_client_connection;
$self->child_finish_hook;
}
sub close_children {
my $self = shift;
$self->SUPER::close_children(@_);
check_sigs(); # since we have captured signals - make sure we handle them
register_sig(PIPE => 'DEFAULT',
INT => 'DEFAULT',
TERM => 'DEFAULT',
QUIT => 'DEFAULT',
HUP => 'DEFAULT',
CHLD => 'DEFAULT',
TTIN => 'DEFAULT',
TTOU => 'DEFAULT',
);
}
1;
__END__
=head1 NAME
Net::Server::Fork - Net::Server personality
=head1 SYNOPSIS
use base qw(Net::Server::Fork);
sub process_request {
#...code...
}
__PACKAGE__->run();
=head1 DESCRIPTION
Please read the pod on Net::Server first. This module is a
personality, or extension, or sub class, of the Net::Server module.
This personality binds to one or more ports and then waits for a
client connection. When a connection is received, the server forks a
child. The child handles the request and then closes.
With the exception of parent/child signaling, this module will work
(with basic functionality) on Win32 systems.
=head1 ARGUMENTS
=over 4
=item check_for_dead
Number of seconds to wait before looking for dead children. This only
takes place if the maximum number of child processes (max_servers) has
been reached. Default is 60 seconds.
=item max_servers
The maximum number of children to fork. The server will not accept
connections until there are free children. Default is 256 children.
=item max_dequeue
The maximum number of dequeue processes to start. If a value of zero
or undef is given, no dequeue processes will be started. The number
of running dequeue processes will be checked by the check_for_dead
variable.
=item check_for_dequeue
Seconds to wait before forking off a dequeue process. It is intended
to use the dequeue process to take care of items such as mail queues.
If a value of undef is given, no dequeue processes will be started.
=back
=head1 CONFIGURATION FILE
See L.
=head1 PROCESS FLOW
Process flow follows Net::Server until the post_accept phase. At this
point a child is forked. The parent is immediately able to wait for
another request. The child handles the request and then exits.
=head1 HOOKS
The Fork server has the following hooks in addition to the hooks
provided by the Net::Server base class. See L
=over 4
=item C<$self-Epre_accept_hook()>
This hook occurs just before the accept is called.
=item C<$self-Epost_accept_hook()>
This hook occurs in the child after the accept and fork.
=item C<$self-Erun_dequeue()>
This hook only gets called in conjunction with the check_for_dequeue
setting.
=back
=head1 HOT DEPLOY
Since version 2.000, the Fork server has accepted the TTIN and TTOU
signals. When a TTIN is received, the max_servers is increased by 1.
If a TTOU signal is received the max_servers is decreased by 1. This
allows for adjusting the number of handling processes without having
to restart the server.
=head1 AUTHOR
Paul Seamons
Rob Brown
=head1 SEE ALSO
Please see also
L,
L,
L,
L
L
=cut
Net-Server-2.008/lib/Net/Server/MultiType.pm 0000644 0001750 0001750 00000012376 12331755703 017254 0 ustar paul paul # -*- perl -*-
#
# Net::Server::MultiType - Net::Server personality
#
# $Id$
#
# Copyright (C) 2001-2012
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
# All rights reserved.
#
################################################################
package Net::Server::MultiType;
use strict;
use base qw(Net::Server);
#sub net_server_type { shift->SUPER::net_server_type }; # not-needed
sub options {
my $self = shift;
my $ref = $self->SUPER::options(@_);
$ref->{'server_type'} = $self->{'server'}->{'server_type'} ||= [];
return $ref;
}
sub default_server_type { 'Fork' }
sub run {
my $self = ref($_[0]) ? shift() : shift->new;
$self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_];
$self->_initialize;
my $prop = $self->{'server'};
if (!defined $prop->{'server_type'} || ! @{ $prop->{'server_type'} }) {
if (my $ref = $self->can('default_server_type') && $self->default_server_type) {
$prop->{'server_type'} = ref($ref) ? $ref : [$ref];
}
}
foreach my $type (@{ $prop->{'server_type'} || []}) {
next if $type eq 'MultiType';
$type = ($type =~ /^(\w+)$/) ? $1 : next; # satisfy taint
my $pkg = ($type =~ /::/) ? $type : "Net::Server::$type";
(my $file = "$pkg.pm") =~ s{::}{/}g;
eval { require $file };
if ($@){
warn "Couldn't become server type \"$pkg\" [$@]\n";
next;
}
# handle items like HTTP and PSGI that aren't true Net::Server flavors, but themselves are MultiType
if ($pkg->isa(__PACKAGE__)) {
my $type = $self->default_server_type || 'Single';
$type = ($type =~ /^(\w+)$/) ? $1 : next; # satisfy taint
my $_pkg = ($type =~ /::/) ? $type : "Net::Server::$type";
$prop->{'_recursive_multitype'} = $_pkg;
(my $file = "$_pkg.pm") =~ s{::}{/}g;
eval { require $file } or die "Trouble becoming server type $pkg while loading default package $_pkg: $@\n";
die "Recursive inheritance - Package $pkg inherits from $_pkg.\n" if $_pkg->isa($pkg);
no strict 'refs';
@{"${pkg}::ISA"} = ($_pkg);
}
# cludgy - doesn't allow multiple Net::Server::MultiType servers within same process
# but it is probably better than modifying our child's class for it
@Net::Server::MultiType::ISA = ($pkg);
last;
}
# now run as the new type of thingy
# passing self, instead of package, doesn't instantiate a new object
$self->SUPER::run(@_);
}
1;
__END__
=head1 NAME
Net::Server::MultiType - Net::Server personality
=head1 SYNOPSIS
use base qw(Net::Server::MultiType);
sub process_request {
#...code...
}
my @types = qw(PreFork Fork Single);
Net::Server::MultiType->run(server_type => \@types);
=head1 DESCRIPTION
Please read the pod on Net::Server first. This module is a
personality, or extension, or sub class, of the Net::Server module.
This personality is intended to allow for easy use of multiple
Net::Server personalities. Given a list of server types,
Net::Server::MultiType will require one at a time until it finds one
that is installed on the system. It then adds that package to its
@ISA, thus inheriting the methods of that personality.
=head1 ARGUMENTS
In addition to the command line arguments of the Net::Server base
class, Net::Server::MultiType contains one other configurable
parameter.
Key Value Default
server_type 'server_type' 'Single'
=over 4
=item server_type
May be called many times to build up an array or possible
server_types. At execution, Net::Server::MultiType will find the
first available one and then inherit the methods of that personality
=back
=head1 CONFIGURATION FILE
C allows for the use of a configuration file
to read in server parameters. The format of this conf file is simple
key value pairs. Comments and white space are ignored.
#-------------- file test.conf --------------
### multi type info
### try PreFork first, then go to Single
server_type PreFork
server_type Single
### server information
min_servers 20
max_servers 80
spare_servers 10
max_requests 1000
### user and group to become
user somebody
group everybody
### logging ?
log_file /var/log/server.log
log_level 3
pid_file /tmp/server.pid
### access control
allow .+\.(net|com)
allow domain\.com
deny a.+
### background the process?
background 1
### ports to bind
host 127.0.0.1
port localhost:20204
port 20205
### reverse lookups ?
# reverse_lookups on
#-------------- file test.conf --------------
=head1 PROCESS FLOW
See L
=head1 HOOKS
There are no additional hooks in Net::Server::MultiType.
=head1 TO DO
See L
=head1 AUTHOR
Paul T. Seamons paul@seamons.com
=head1 SEE ALSO
Please see also
L,
L,
L,
L,
L
=cut
Net-Server-2.008/lib/Net/Server/Multiplex.pm 0000644 0001750 0001750 00000036257 12331755703 017307 0 ustar paul paul # -*- perl -*-
#
# Net::Server::Multiplex - Net::Server personality
#
# $Id$
#
# Copyright (C) 2001-2012
#
# Rob Brown bbb@cpan,org
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
################################################################
package Net::Server::Multiplex;
use strict;
use base qw(Net::Server);
use Net::Server::SIG qw(register_sig check_sigs);
use Carp qw(confess);
eval { require IO::Multiplex; import IO::Multiplex 1.05; };
$@ && warn "Module IO::Multiplex is required for Multiplex.";
our $VERSION = $Net::Server::VERSION;
sub net_server_type { __PACKAGE__ }
sub loop {
my $self = shift;
my $prop = $self->{server};
my $mux = IO::Multiplex->new;
$self->{mux} = $mux;
foreach my $sock ( @{ $prop->{sock} } ) {
if (Net::Server::SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(),Socket::SO_TYPE())) {
$mux->add($sock);
} else {
$mux->listen($sock);
}
}
$mux->set_callback_object(Net::Server::Multiplex::MUX->init($self));
###
### Use Net::Server::SIG for safe signal handling.
###
### register some of the signals for safe handling
register_sig(PIPE => sub { $self->log(4, "SIG$_[0] received") },
INT => sub { $self->server_close() },
TERM => sub { $self->server_close() },
QUIT => sub { $self->server_close() },
HUP => sub { $self->sig_hup() },
CHLD => sub { $self->sig_chld() },
);
if ( defined $prop->{check_for_dequeue} ) {
# It does not matter which socket the timeout is associated with.
$mux->set_timeout( $prop->{sock}->[0], $prop->{check_for_dequeue} );
}
$mux->loop(sub {
my ($rdready, $wrready) = @_;
check_sigs();
$mux->endloop if $prop->{_HUP};
});
### fall back to the main run routine
}
### make sure that we properly disconnect from the mux if we are HUPing
sub sig_hup {
my $self = shift;
my $prop = $self->{server};
if (my $mux = $self->{mux}) {
foreach my $sock ( @{ $prop->{sock} } ){
$mux->remove($sock);
}
}
return $self->SUPER::sig_hup(@_);
}
# This method instead of run_client_connection
# because STDOUT should be tied correctly,
# not just globbed onto the socket. This
# tie is taken care of in the mux_connection
# routine instead of within post_accept.
# Also, the process_request stuff should never be
# used since the request should be really processed
# via mux_* methods.
sub setup_client_connection {
my ($self, $mux) = @_;
my $prop = $self->{server};
### Copied from Net::Server::post_accept...
$prop->{requests} ++;
if (! $prop->{no_client_stdout}) {
*STDIN = \*{ $prop->{client} };
# *STDOUT = \*{ $prop->{client} };
# STDIN->autoflush(1);
}
### Copied from Net::Server::run_client_connection...
$self->get_client_info; # determines information about peer and local
$self->post_accept_hook; # user customizable hook
unless($self->allow_deny && # do allow/deny check on client info
$self->allow_deny_hook ){ # user customizable hook
$self->request_denied_hook; # user customizable hook
# Flush output buffer and close connection since it should be denied.
if (! $prop->{no_client_stdout}) {
close (STDOUT);
}
return 0;
}
return 1;
}
# Compatibility interface for Net::Server
sub run_dequeue {
confess "&$Net::Server::Multiplex::MUX::ISA[0]\::run_dequeue never defined";
}
sub mux_connection {}
sub mux_input {
confess "&$Net::Server::Multiplex::MUX::ISA[0]\::mux_input never defined";
}
sub mux_eof {}
sub mux_close {}
sub mux_timeout {
confess "&$Net::Server::Multiplex::MUX::ISA[0]\::mux_timeout never defined";
}
package Net::Server::Multiplex::MUX;
# Just a dumb module to be used for the
# Multiplex callback_object hooks
use strict;
our $VERSION = $Net::Server::Multiplex::VERSION;
# This temporary @ISA should always be overridden
# at runtime when init() is called. This module should
# really ISA whatever module ISA Net::Server::Multiplex.
our @ISA = qw(Net::Server::Multiplex);
# This subroutine is meant to create the main callback
# object to be used for all listen file descriptors.
# It just needs to make sure the {net_server} property
# is set.
sub init {
my $package = shift;
my $net_server= shift;
# On-the-fly runtime molymorphism hack
# to ISA the same type of thing passed.
@ISA = (ref $net_server);
my $self = bless {
net_server => $net_server,
} => $package;
return $self;
}
# The new() routine is passed the Net::Server object. It
# is meant to create the client specific callback object.
# Note that the $net_server->{server} property hash may be
# modified by future connections through Net::Server.
# Any values within it that this object may need to use
# later must be copied within itself.
sub new {
my $package = shift;
my $net_server = shift;
my $self = bless {
# Some nice values to remember for this client
net_server => $net_server,
peeraddr => $net_server->{server}->{peeraddr},
connected => time,
}, $package;
return $self;
}
sub log { shift->{net_server}->log(@_) }
# This subroutine is only used by the listen callback object.
sub mux_connection {
my ($self, $mux, $fh) = @_;
my $net_server = $self->{net_server};
$net_server->{server}->{client} = $fh;
$self->_link_stdout($mux, $fh);
if ($net_server->setup_client_connection($mux)) {
# Create client specific callback object
my $client_object = Net::Server::Multiplex::MUX->new($net_server, $fh);
# Set this as the callback object for this client
$mux->set_callback_object($client_object, $fh);
# Finally call the clients real mux_connection routine,
# if any. This allows all the mux_* routines to be
# called from the same type of object.
$client_object->SUPER::mux_connection($mux, $fh);
#$client_object->mux_connection($mux, $fh);
}
$self->_unlink_stdout();
return;
}
sub mux_input {
my ($self, $mux, $fh, $in_ref) = @_;
$self->_link_stdout($mux, $fh);
$self->SUPER::mux_input($mux, $fh, $in_ref);
$self->_unlink_stdout();
return;
}
sub mux_eof {
my ($self, $mux, $fh, $in_ref) = @_;
$self->_link_stdout($mux, $fh);
$self->SUPER::mux_eof($mux, $fh, $in_ref);
$self->_unlink_stdout();
$mux->shutdown($fh, 1);
return;
}
sub mux_close {
my ($self, $mux, $fh) = @_;
$self->{net_server}->post_process_request_hook;
$self->SUPER::mux_close($mux, $fh);
return;
}
sub mux_timeout {
my ($self, $mux, $fh) = @_;
if ( my $check = $self->{net_server}->{server}->{check_for_dequeue} ) {
$self->{net_server}->run_dequeue();
$mux->set_timeout( $fh, $check );
} else {
$self->_link_stdout($mux, $fh);
$self->SUPER::mux_timeout($mux, $fh);
$self->_unlink_stdout();
}
return;
}
sub _link_stdout {
my ($self, $mux, $fh) = @_;
return if $self->{net_server}->{server}->{no_client_stdout};
# Hook up STDOUT to the correct socket
if (tied *$fh) {
# Make sure STDOUT is tied however $fh is
tie (*STDOUT, (ref tied *$fh), $mux, $fh);
} else {
*STDOUT = *$fh;
}
}
sub _unlink_stdout {
my $self = shift;
return if $self->{net_server}->{server}->{no_client_stdout};
my $x = tied *STDOUT;
if ($x) {
undef $x;
untie *STDOUT;
}
}
1;
__END__
=head1 NAME
Net::Server::Multiplex - Multiplex several connections within one process
=head1 SYNOPSIS
package MyPlexer;
use base qw(Net::Server::Multiplex);
sub mux_input {
#...code...
}
__PACKAGE__->run();
=head1 DESCRIPTION
This personality is designed to handle multiple connections all within
one process. It should only be used with protocols that are
guaranteed to be able to respond quickly on a packet by packet basis.
If determining a response could take a while or an unknown period of
time, all other connections established will block until the response
completes. If this condition might ever occur, this personality
should probably not be used.
This takes some nice features of Net::Server (like the server listen
socket setup, configuration file processing, safe signal handling,
convenient inet style STDIN/STDOUT handling, logging features,
deamonization and pid tracking, and restartability -SIGHUP) and some
nice features of IO::Multiplex (automatic buffered IO and
per-file-handle objects) and combines them for an easy-to-use
interace.
See examples/samplechat.pl distributed with Net::Server for a simple
chat server that uses several of these features.
=head1 PROCESS FLOW
The process flow is written in an open, easy to override, easy to
hook, fashion. The basic flow is shown below.
$self->configure_hook;
$self->configure(@_);
$self->post_configure;
$self->post_configure_hook;
$self->pre_bind;
$self->bind;
if (Restarting server) {
$self->restart_open_hook();
}
$self->post_bind_hook;
$self->post_bind;
$self->pre_loop_hook;
$self->loop; # This basically just runs IO::Multiplex::loop
# For routines inside a $self->loop
# See CLIENT PROCESSING below
$self->pre_server_close_hook;
$self->post_child_cleanup_hook;
$self->server_close;
if (Restarting server) {
$self->restart_close_hook();
$self->hup_server;
# Redo process again starting with configure_hook
}
The server then exits.
=head1 CLIENT PROCESSING
The following represents the client processing program flow:
$self->{server}->{client} = Net::Server::Proto::TCP->accept(); # NOTE: Multiplexed with mux_input() below
if (check_for_dequeue seconds have passed) {
$self->run_dequeue();
}
$self->get_client_info;
$self->post_accept_hook; # Net::Server style
if ($self->allow_deny
&& $self->allow_deny_hook) {
# (Net::Server style $self->process_request() is never called.)
# A unique client specific object is created
# for all mux_* methods from this point on.
$self = __PACKAGE__->new($self, client);
$self->mux_connection; # IO::Multiplex style
for (every packet received) {
$self->mux_input; # NOTE: Multiplexed with accept() above
}
} else {
$self->request_denied_hook;
# Notice that if either allow_deny or allow_deny_hook fails, then
# new(), mux_connection(), and mux_input() will never be called.
# mux_eof() and mux_close() will still be called, but using a
# common listen socket callback object instead of a unique client
# specific object.
}
$self->mux_eof;
$self->post_process_request_hook;
$self->mux_close;
This process then loops multiplexing between the accept() for the next
connection and mux_input() when input arrives to avoid blocking either
one.
=head1 HOOKS
The *_hook methods mentioned above are meant to be overridden with
your own subroutines if you desire to provide additional
functionality.
The loop() method of Net::Server has been overridden to run the loop
routine of IO::Multiplex instead. The Net::Server methods may access
the IO::Multiplex object at C<$self-E{mux}> if desired. The
IO::Multiplex methods may access the Net::Server object at
C<$self-E{net_server}> if desired.
The process_request() method is never used with this personality.
The other Net::Server hooks and methods should work the same.
=over 4
=item C<$self-Erun_dequeue()>
This hook only gets called in conjunction with the check_for_dequeue
setting. It will run every check_for_dequeue seconds. Since no
forking is done, this hook should run fast in order to prevent
blocking the rest of the processing.
=back
=head1 TIMEOUTS
=head2 set_timeout
To utilize the optional timeout feature of IO::Multiplex, you need to
specify a timeout by using the set_timeout method.
$self->{net_server}->{mux}->set_timeout($fh, $seconds_from_now);
$fh may be either a client socket or a listen socket file descriptor
within the mux. $seconds_from_now may be fractional to achieve more
precise timeouts. This is used in conjunction with mux_timeout, which
you should define yourself.
=head2 mux_timeout
The main loop() routine will call $obj->mux_timeout($mux, $fh) when
the timeout specified in set_timeout is reached where $fh is the same
as the one specified in set_timeout() and $obj is its corresponding
object (either the unique client specific object or the main listen
callback object) and $mux is the main IO::Multiplex object itself.
=head1 CALLBACK INTERFACE
Callback objects should support the following interface. You do not
have to provide all of these methods, just provide the ones you are
interested in. These are just like the IO::Multiplex hooks except
that STDOUT is tied to the corresponding client socket handle for your
convenience and to more closely emulate the Net::Server model.
However, unlike some other Net::Server personalities, you should never
read directly from STDIN yourself. You should define one or more of
the following methods:
=head2 mux_connection ($mux,$fh)
(OPTIONAL) Run once when the client first connects if the allow_deny
passes. Note that the C<$self-E{net_server}-E{server}>
property hash may be modified by future connections through
Net::Server. Any values within it that this object may need to use
later should be copied within its own object at this point.
Example:
$self->{peerport} = $self->{net_server}->{server}->{peerport};
=head2 mux_input ($mux,$fh,\$data)
(REQUIRED) Run each time a packet is read. It should consume $data
starting at the left and leave unconsumed data in the scalar for
future calls to mux_input.
=head2 mux_eof ($mux,$fh,\$data)
(OPTIONAL) Run once when the client is done writing. It should
consume the rest of $data since mux_input() will never be run again.
=head2 mux_close ($mux,$fh)
(OPTIONAL) Run after the entire client socket has been closed. No
more attempts should be made to read or write to the client or to
STDOUT.
=head2 mux_timeout ($mux,$fh)
(OPTIONAL) Run once when the set_timeout setting expires as explained
above.
=head1 BUGS
This is only known to work with TCP servers.
If you need to use the IO::Multiplex style set_timeout / mux_timeout
interface, you cannot use the Net::Server style check_for_dequeue /
run_dequeue interface. It will not work if the check_for_dequeue
option is specified. The run_dequeue method is just a compatibility
interface to comply with the Net::Server::Fork style run_dequeue but
is implemented in terms of the IO::Multiplex style set_timeout and
mux_timeout methods.
=head1 AUTHOR
Rob Brown
=head1 MAINTAINER
Paul Seamons
=head1 LICENSE
This package may be distributed under the terms of either the
GNU General Public License
or the
Perl Artistic License
All rights reserved.
=head1 SEE ALSO
L by Paul Seamons ,
L by Bruce Keeler .
=cut
Net-Server-2.008/lib/Net/Server/PreForkSimple.pm 0000644 0001750 0001750 00000042411 12331755703 020033 0 ustar paul paul # -*- perl -*-
#
# Net::Server::PreForkSimple - Net::Server personality
#
# $Id$
#
# Copyright (C) 2001-2012
#
# Paul Seamons
# paul@seamons.com
# http://seamons.com/
#
# This package may be distributed under the terms of either the
# GNU General Public License
# or the
# Perl Artistic License
#
# All rights reserved.
#
################################################################
package Net::Server::PreForkSimple;
use strict;
use base qw(Net::Server);
use Net::Server::SIG qw(register_sig check_sigs);
use POSIX qw(WNOHANG EINTR);
use Fcntl ();
sub net_server_type { __PACKAGE__ }
sub options {
my $self = shift;
my $ref = $self->SUPER::options(@_);
my $prop = $self->{'server'};
$ref->{$_} = \$prop->{$_} for qw(max_servers max_requests max_dequeue
check_for_dead check_for_dequeue
lock_file serialize);
$ref->{'sig_passthrough'} = $prop->{'sig_passthrough'} = [];
return $ref;
}
sub post_configure {
my $self = shift;
my $prop = $self->{'server'};
$self->SUPER::post_configure;
### some default values to check for
my $d = {
max_servers => 50, # max num of servers to run
max_requests => 1000, # num of requests for each child to handle
check_for_dead => 30, # how often to see if children are alive
};
foreach (keys %$d){
$prop->{$_} = $d->{$_}
unless defined($prop->{$_}) && $prop->{$_} =~ /^\d+$/;
}
$prop->{'ppid'} = $$;
}
sub post_bind {
my $self = shift;
my $prop = $self->{'server'};
$self->SUPER::post_bind;
if ($prop->{'multi_port'} && $prop->{'serialize'} && $prop->{'serialize'} eq 'none') {
$self->log(2, "Passed serialize value of none is incompatible with multiple ports - using default serialize");
delete $prop->{'serialize'};
}
if (!$prop->{'serialize'}
|| $prop->{'serialize'} !~ /^(flock|semaphore|pipe|none)$/i) {
$prop->{'serialize'} = ($^O eq 'MSWin32') ? 'pipe' : 'flock';
}
$prop->{'serialize'} =~ tr/A-Z/a-z/;
if ($prop->{'serialize'} eq 'flock') {
$self->log(3, "Setting up serialization via flock");
if (defined $prop->{'lock_file'}) {
$prop->{'lock_file_unlink'} = undef;
} else {
$prop->{'lock_file'} = eval { require File::Temp } ? File::Temp::tmpnam() : POSIX::tmpnam();
$prop->{'lock_file_unlink'} = 1;
}
} elsif ($prop->{'serialize'} eq 'semaphore') {
$self->log(3, "Setting up serialization via semaphore");
require IPC::SysV;
require IPC::Semaphore;
my $s = IPC::Semaphore->new(IPC::SysV::IPC_PRIVATE(), 1, IPC::SysV::S_IRWXU() | IPC::SysV::IPC_CREAT())
or $self->fatal("Semaphore error [$!]");
$s->setall(1) or $self->fatal("Semaphore create error [$!]");
$prop->{'sem'} = $s;
} elsif ($prop->{'serialize'} eq 'pipe') {
$self->log(3, "Setting up serialization via pipe");
pipe(my $waiting, my $ready);
$ready->autoflush(1);
$waiting->autoflush(1);
$prop->{'_READY'} = $ready;
$prop->{'_WAITING'} = $waiting;
print $ready "First\n";
} elsif ($prop->{'serialize'} eq 'none') {
$self->log(3, "Using no serialization");
} else {
$self->fatal("Unknown serialization type \"$prop->{'serialize'}\"");
}
}
sub loop {
my $self = shift;
my $prop = $self->{'server'};
$prop->{'children'} = {};
if ($ENV{'HUP_CHILDREN'}) {
my %children = map {/^(\w+)$/; $1} split(/\s+/, $ENV{'HUP_CHILDREN'});
$children{$_} = {status => $children{$_}, hup => 1} foreach keys %children;
$prop->{'children'} = \%children;
}
$self->log(3, "Beginning prefork ($prop->{'max_servers'} processes)");
$self->run_n_children($prop->{'max_servers'});
$self->run_parent;
}
sub run_n_children {
my ($self, $n) = @_;
return if $n <= 0;
my $prop = $self->{'server'};
$self->run_n_children_hook;
$self->log(3, "Starting \"$n\" children");
for (1 .. $n) {
$self->pre_fork_hook;
local $!;
my $pid = fork;
$self->fatal("Bad fork [$!]") if ! defined $pid;
if ($pid) {
$prop->{'children'}->{$pid}->{'status'} = 'processing';
} else {
$self->run_child;
}
}
}
sub run_n_children_hook {}
sub run_child {
my $self = shift;
my $prop = $self->{'server'};
$SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub {
$self->child_finish_hook;
exit;
};
$SIG{'PIPE'} = 'IGNORE';
$SIG{'CHLD'} = 'DEFAULT';
$SIG{'HUP'} = sub {
if (! $prop->{'connected'}) {
$self->child_finish_hook;
exit;
}
$prop->{'SigHUPed'} = 1;
};
my $needs_lock = ($prop->{'serialize'} eq 'flock') ? 1 : 0;
if ($needs_lock) {
open($prop->{'lock_fh'}, ">", $prop->{'lock_file'})
or $self->fatal("Couldn't open lock file \"$prop->{'lock_file'}\"[$!]");
}
$self->log(4, "Child Preforked ($$)");
delete $prop->{'children'};
$self->child_init_hook;
while ($self->accept()) {
$prop->{'connected'} = 1;
$self->run_client_connection;
$prop->{'connected'} = 0;
last if $self->done;
}
$self->child_finish_hook;
close($prop->{'lock_fh'}) if $needs_lock && $prop->{'lock_fh'};
$self->log(4, "Child leaving ($prop->{'max_requests'})");
exit;
}
sub is_prefork { 1 }
### We can only let one process do the selecting at a time
### this override makes sure that nobody else can do it
### while we are. We do this either by opening a lock file
### and getting an exclusive lock (this will block all others
### until we release it) or by using semaphores to block
sub accept {
my $self = shift;
my $prop = $self->{'server'};
if ($prop->{'serialize'} eq 'flock') {
while (! flock $prop->{'lock_fh'}, Fcntl::LOCK_EX()) {
next if $! == EINTR;
$self->fatal("Couldn't get lock on file \"$prop->{'lock_file'}\" [$!]");
}
my $v = $self->SUPER::accept();
flock $prop->{'lock_fh'}, Fcntl::LOCK_UN();
return $v;
} elsif ($prop->{'serialize'} eq 'semaphore') {
$prop->{'sem'}->op(0, -1, IPC::SysV::SEM_UNDO()) or $self->fatal("Semaphore Error [$!]");
my $v = $self->SUPER::accept();
$prop->{'sem'}->op(0, 1, IPC::SysV::SEM_UNDO()) or $self->fatal("Semaphore Error [$!]");
return $v;
} elsif ($prop->{'serialize'} eq 'pipe') {
my $waiting = $prop->{'_WAITING'};
scalar <$waiting>; # read one line - kernel says who gets it
my $v = $self->SUPER::accept();
print { $prop->{'_READY'} } "Next!\n";
return $v;
} else {
my $v = $self->SUPER::accept();
return $v;
}
}
sub done {
my $self = shift;
my $prop = $self->{'server'};
$prop->{'done'} = shift if @_;
return 1 if $prop->{'done'};
return 1 if $prop->{'requests'} >= $prop->{'max_requests'};
return 1 if $prop->{'SigHUPed'};
if (! kill 0, $prop->{'ppid'}) {
$self->log(3, "Parent process gone away. Shutting down");
return 1;
}
}
sub run_parent {
my $self=shift;
my $prop = $self->{'server'};
$self->log(4, "Parent ready for children.");
$prop->{'last_checked_for_dead'} = $prop->{'last_checked_for_dequeue'} = time();
register_sig(
PIPE => 'IGNORE',
INT => sub { $self->server_close() },
TERM => sub { $self->server_close() },
HUP => sub { $self->sig_hup() },
CHLD => sub {
while (defined(my $chld = waitpid(-1, WNOHANG))) {
last unless $chld > 0;
$self->delete_child($chld);
}
},
QUIT => sub { $self->{'server'}->{'kind_quit'} = 1; $self->server_close() },
TTIN => sub { $self->{'server'}->{'max_servers'}++; $self->log(3, "Increasing max server count ($self->{'server'}->{'max_servers'})") },
TTOU => sub {
$self->{'server'}->{'max_servers'}--;
$self->log(3, "Decreasing max server count ($self->{'server'}->{'max_servers'})");
if (defined(my $pid = each %{ $prop->{'children'} })) {
$self->delete_child($pid) if ! kill('HUP', $pid);
}
},
);
$self->register_sig_pass;
if ($ENV{'HUP_CHILDREN'}) {
while (defined(my $chld = waitpid(-1, WNOHANG))) {
last unless $chld > 0;
$self->delete_child($chld);
}
}
while (1) {
select undef, undef, undef, 10;
if (check_sigs()){
last if $prop->{'_HUP'};
}
$self->idle_loop_hook();
# periodically make sure children are alive
my $time = time();
if ($time - $prop->{'last_checked_for_dead'} > $prop->{'check_for_dead'}) {
$prop->{'last_checked_for_dead'} = $time;
foreach (keys %{ $prop->{'children'} }) {
kill(0,$_) or $self->delete_child($_);
}
}
# make sure we always have max_servers
my $total_n = 0;
my $total_d = 0;
foreach (values %{ $prop->{'children'} }){
if( $_->{'status'} eq 'dequeue' ){
$total_d ++;
}else{
$total_n ++;
}
}
if( $prop->{'max_servers'} > $total_n ){
$self->run_n_children( $prop->{'max_servers'} - $total_n );
}
# periodically check to see if we should clear the queue
if( defined $prop->{'check_for_dequeue'} ){
if( $time - $prop->{'last_checked_for_dequeue'}
> $prop->{'check_for_dequeue'} ){
$prop->{'last_checked_for_dequeue'} = $time;
if( defined($prop->{'max_dequeue'})
&& $total_d < $prop->{'max_dequeue'} ){
$self->run_dequeue();
}
}
}
}
}
sub idle_loop_hook {}
sub close_children {
my $self = shift;
$self->SUPER::close_children(@_);
check_sigs(); # since we have captured signals - make sure we handle them
register_sig(PIPE => 'DEFAULT',
INT => 'DEFAULT',
TERM => 'DEFAULT',
QUIT => 'DEFAULT',
HUP => 'DEFAULT',
CHLD => 'DEFAULT',
TTIN => 'DEFAULT',
TTOU => 'DEFAULT',
);
}
1;
__END__
=head1 NAME
Net::Server::PreForkSimple - Net::Server personality
=head1 SYNOPSIS
use base qw(Net::Server::PreForkSimple);
sub process_request {
#...code...
}
__PACKAGE__->run();
=head1 DESCRIPTION
Please read the pod on Net::Server first. This module is a
personality, or extension, or sub class, of the Net::Server module.
This personality binds to one or more ports and then forks
C child processes. The server will make sure that at any
given time there are always C available to receive a
client request. Each of these children will process up to
C client connections. This type is good for a heavily
hit site that can keep C processes dedicated to the
serving. (Multi port accept defaults to using flock to serialize the
children).
At this time, it does not appear that this module will pass tests on
Win32 systems. Any ideas or patches for making the tests pass would
be welcome.
=head1 SAMPLE CODE
Please see the sample listed in Net::Server.
=head1 COMMAND LINE ARGUMENTS
In addition to the command line arguments of the Net::Server base
class, Net::Server::PreFork contains several other configurable
parameters.
Key Value Default
max_servers \d+ 50
max_requests \d+ 1000
serialize (flock|semaphore
|pipe|none) undef
# serialize defaults to flock on multi_port or on Solaris
lock_file "filename" File::Temp::tempfile or POSIX::tmpnam
check_for_dead \d+ 30
max_dequeue \d+ undef
check_for_dequeue \d+ undef
=over 4
=item max_servers
The maximum number of child servers to start and maintain. This does
not apply to dequeue processes.
=item max_requests
The number of client connections to receive before a child terminates.
=item serialize
Determines whether the server serializes child connections. Options
are undef, flock, semaphore, pipe, or none. Default is undef. On
multi_port servers or on servers running on Solaris, the default is
flock. The flock option uses blocking exclusive flock on the file
specified in I (see below). The semaphore option uses
IPC::Semaphore (thanks to Bennett Todd) for giving some sample code.
The pipe option reads on a pipe to choose the next. the flock option
should be the most bulletproof while the pipe option should be the
most portable. (Flock is able to reliquish the block if the process
dies between accept on the socket and reading of the client connection
- semaphore and pipe do not). An option of none will not perform
any serialization. If "none" is passed and there are multiple ports
then a the default serialization will be used insted of "none."
=item lock_file
Filename to use in flock serialized accept in order to serialize the
accept sequece between the children. This will default to a generated
temporary filename. If default value is used the lock_file will be
removed when the server closes.
=item check_for_dead
Seconds to wait before checking to see if a child died without letting
the parent know.
=item max_dequeue
The maximum number of dequeue processes to start. If a value of zero
or undef is given, no dequeue processes will be started. The number
of running dequeue processes will be checked by the check_for_dead
variable.
=item check_for_dequeue
Seconds to wait before forking off a dequeue process. The run_dequeue
hook must be defined when using this setting. It is intended to use
the dequeue process to take care of items such as mail queues. If a
value of undef is given, no dequeue processes will be started.
=back
=head1 CONFIGURATION FILE
C