I have a Perl LDAP server which is based on the use
Net::LDAP::Server
module.
To have it running as a service in Linux, I use the
Net::Server::PreForkSimple
library.
So far everything works fine, but when I shut the service down the child processes don't close.
What is the correct way to shut down all processes?
When I send a SIGTERM
signal, the server_close
method is called but nothing else happens. From this point on, terminated children are no longer restarted, but the children waiting for work are not terminated.
#!/usr/bin/perl
#chkconfig: 345 80 05
#description: LDAP Server for Innovaphone
use strict;
use warnings;
package Listener;
use Net::Server;
use base 'Net::Server::PreForkSimple';
my $continue = 1;
...
sub process_request {
my $self = shift;
my $in = *STDIN{IO};
my $out = *STDOUT{IO};
my $sock = $self->{server}->{client};
my $peer_address = $sock->peerhost();
my $peer_port = $sock->peerport();
logwarn( "Connection accepted from $peer_address : $peer_port" );
my $handler = InnoLdapServer->new($sock);
while ( 1 ) {
my $finished = $handler->handle;
return if $finished;
}
}
...
sub server_close {
logmsg( "Server close called" );
$continue = 0;
}
# Start daemon
Proc::Daemon::Init();
my $pidfile = File::Pid->new({ file => "/var/run/inno-ldap.pid" });
if ( $pidfile->running() ) {
die "Already running";
}
$pidfile->write();
open( STDOUT, '>', "$logpath/inno-ldap.$logName.log" ) or die "Can't open stdout log";
select( ( select( STDOUT ), $| = 1 )[0] ); # make the log file "hot" - turn off buffering
open( STDERR, '>', "$logpath/inno-ldap.$logName.error.log" ) or die "Can't open error log";
select( ( select( STDERR ), $| = 1 )[0] ); # make the log file "hot" - turn off buffering
while ( $continue ) {
# package main;
$roothandler = Listener->run(
port => [ 636, "389/tcp" ],
proto => "ssl", # use ssl as the default
ipv => "*", # bind both IPv4 and IPv6 interfaces
user => "daemon",
group => "daemon",
SSL_key_file => "/home/root/ssl_cert/server.pem",
SSL_cert_file => "/home/root/ssl_cert/server.pem",
max_servers => 10,
log_level => 4
);
}
$pidfile->remove();
1;
The shown code doesn't deal with the Net::Server
itself, ie. its ::PreForkSimple
extension; the server itself need be closed if the program is terminated by a signal. We can't see the calling code and how you "shut the service down," other than by the mentioned signal.
There is a server_close
method in Net::Server
for this. But the method of the same name that the question refers to seems to be the one in your Listener
class, which thus overrides the (grand)parent's method. It only stops further work in child processes and doesn't touch the server.
So if that's the method called in the signal handler then the server never gets shut down properly and its existing children stay around. You need to call server_close
on a Net::Server
object, or to arrange in your own server_close
method to call the parent's one.
The Net::Server
documentation under the Process Flow explains
During the server shutdown phase (
$self->server_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;
If the call to server_close
on a Net::Server
object or on the parent of your Listener
object for some reason doesn't do it then try with individual methods listed in the workflow.
There are also signals listed in the Net::Server::PreForkSimple "personality" under 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.
and then under Hot Deploy
Since version 2.000, the PreForkSimple 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
But I'd hope that this shouldn't be needed for shutting it down, and certainly not the last one.