develooper Front page | perl.qpsmtpd | Postings from August 2007

Bugs in qpsmtpd-prefork + some features + patches

Thread Next
From:
Stefan Priebe
Date:
August 29, 2007 23:51
Subject:
Bugs in qpsmtpd-prefork + some features + patches
Message ID:
46D668C0.3020200@prie.be
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

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About