Hello!
I would like to try it again. Two month ago, when 0.40 was released i
also tried to find someone who is interested in resolving bugs from the
qpsmtpd-prefork - but nobody cares. So i hope this time somebody cares
about it. Cause qpsmtpd-prefork is not usable at all on high load / many
connections / second without my patches. Our company has about 500
servers using qpsmtpd-prefork serving more that 50 million connections /
day.
So please please somebody read it and let us discuss about it. I'm
really interested, that qpsmtpd-prefork is getting stable for all.
So here are some problems and the solutions ( the patches may not apply
cleanly to the actual 0.40 version):
1.) You get some errors with the auth mechanism if an old process has
used auth the value seems not to be destroyed. So add
"delete($qpsmtpd->{_auth});"
in function new_child before my ($client, $iinfo) = $d->accept().
otherwise some other hosts are authorized even if they don't authourize
at all.
2.) After some million connections i was getting errors with the
POSIX::dup2(fileno($client), 0) variant for setting the connection to
STDIN and STDOUT (handle errors lost connections etc.). So i changed it
so something more stable (really stable - tested for about 2 month).
Also important was that sometimes in the old Handle was a STRING from
the last SMTP Connection even in the new connection and so
check_earlytalker blocks all. So we have to close the old handle before
opening a new one.
Here is the patch:
-
- # set STDIN/STDOUT and autoflush
- POSIX::dup2(fileno($client), 0)
- || die "unable to duplicate filehandle to STDIN - $!";
- POSIX::dup2(fileno($client), 1)
- || die "unable to duplicate filehandle to STDOUT - $!";
- $| = 1;
+ or die "failed to create new object - $!"; # wait here until
client connects
+
+ next if (!$iinfo or !$client);
+
+ info("connect from: " . ((defined $client->peerhost) ?
$client->peerhost : "unknown") . ":" .
+ ((defined $client->peerport) ?
$client->peerport : "unknown"));
+
+ close(STDIN);
+ open(STDIN, "+<&".fileno($client)) || ::log(LOGERROR, "dup2
failed of client->STDIN");
+ close(STDOUT);
+ open(STDOUT, "+>&".fileno($client)) || ::log(LOGERROR, "dup2
failed of client->STDOUT");
+ select(STDOUT); $|=1;
3.) If you would like to run more than one qpsmtpd-prefork on a single
machine you get a problem with the shared memory so my patch:
# setup shared memory
- $chld_shmem = shmem("qpsmtpd", 1);
+ $chld_shmem = shmem($d_port."qpsmtpd", 1);
untie $chld_shmem;
@@ -470,7 +485,7 @@ sub shmem_opt {
my ($chld_shmem, $chld_busy);
eval {
- $chld_shmem = &shmem("qpsmtpd", 0); #connect to shared
memory hash
+ $chld_shmem = &shmem($d_port."qpsmtpd", 0); #connect to
shared memory hash
if (tied %{$chld_shmem}) {
# perform options
4.) The only last problem i have is, that if i don't exit the child if
it has done a TLS connection a second or third TLS connection mostly fail.
So my dirty hack at the moment is quitting the child if it has done an
tld connection - but be careful it does not check if you use really use
qpsmtpd-prefork.
Patch for plugins/tls
Add:
sub hook_quit {
my $self = shift;
if ($self->connection->notes('tls_enabled')) {
$self->log(LOGERROR, "exit cause of enables tls");
exit;
}
return DECLINED;
}
5.) I liked the idea of renicing but I think the important thing is,
that the master / parent process hold its nice value and only the childs
get a new one. So new connections will be handled fast and only the
child connections are getting slower (if the cpu is at high usage). So
we will not change the nice level in the parent and only in the child
this is already done in "sub new_child {)".
So my patch:
@@ -169,16 +173,17 @@ sub run {
"$d_addr, port: $d_port (user: $user [$<])");
# reset priority
- my $old_nice = getpriority(0, 0);
- my $new_nice = $old_nice - $re_nice;
- if ($new_nice < 20 && $new_nice > -20) {
- setpriority(0, 0, $1) if ($new_nice =~ /(\-?\d+)/);
- info("parent daemon nice level: $1");
- }
- else {
- die "FATAL: new nice level: $new_nice is not between -19 and 19 "
- . "(old level = $old_nice, renice value = $re_nice)";
- }
+ # we do not want to change the nice level of the parent
+ #my $old_nice = getpriority(0, 0);
+ #my $new_nice = $old_nice - $re_nice;
+ #if ($new_nice < 20 && $new_nice > -20) {
+ # setpriority(0, 0, $1) if ($new_nice =~ /(\-?\d+)/);
+ # info("parent daemon nice level: $1");
+ #}
+ #else {
+ # die "FATAL: new nice level: $new_nice is not between -19 and 19 "
+ # . "(old level = $old_nice, renice value = $re_nice)";
+ #}
if ($user) {
# change UUID/UGID
Stefan