This patch is for qpsmtpd-forkserver 0.30, but looks to be easily 
implemented into 0.31 as well.  Basically, this patch creates a
function that is called to check the current load and number of
connections.  Been in production here for a while and seems to be
stable. Let me know what you think. BTW.. The idea for this patch came
from one I saw on the list but I can't remember for the life of me who
wrote the original one.

******** Start qp-fork-maxload.patch *********

--- qpsmtpd-forkserver-orig     2006-01-23 16:36:50.952304966 -0600
+++ qpsmtpd-forkserver  2006-01-26 18:22:44.447540419 -0600
@@ -18,11 +18,12 @@
 $| = 1;

 # Configuration
-my $MAXCONN   = 90;                            # max simultaneous connections
-my $PORT      = 25;                            # port number
-my $LOCALADDR = '0.0.0.0';             # ip address to bind to
-my $USER      = 'smtpd';               # user to suid to
+my $MAXCONN   = 90;            # max simultaneous connections
+my $PORT      = 25;            # port number
+my $LOCALADDR = '0.0.0.0';     # ip address to bind to
+my $USER      = 'smtpd';       # user to suid to
 my $MAXCONNIP = 5;              # max simultaneous connections from one IP
+my $MAXLOAD   = 4.0;            # max load before we stop accepting 
connections, setting of 0.0 will disable

 sub usage {
         print <<"EOT";
@@ -32,6 +33,8 @@
  -c, --limit-connections N : limit concurrent connections to N; default 15
  -u, --user U              : run as a particular user (default 'smtpd')
  -m, --max-from-ip M       : limit connections from a single IP; default 5
+ -a, --max-load F.F        : Stop accepting connections at this load.
+                             A setting of 0.0 will disable this feature; 
default 4.0
 EOT
         exit 0;
 }
@@ -41,13 +44,15 @@
            'c|limit-connections=i' => \$MAXCONN,
            'm|max-from-ip=i' => \$MAXCONNIP,
            'p|port=i' => \$PORT,
-           'u|user=s' => \$USER) || &usage;
+           'u|user=s' => \$USER,
+           'a|max-load=f' => \$MAXLOAD) || &usage;

 # detaint the commandline
 if ($PORT =~ /^(\d+)$/) { $PORT = $1 } else { &usage }
 if ($LOCALADDR =~ /^([\d\w\-.]+)$/) { $LOCALADDR = $1 } else { &usage }
 if ($USER =~ /^([\w\-]+)$/) { $USER = $1 } else { &usage }
 if ($MAXCONN =~ /^(\d+)$/) { $MAXCONN = $1 } else { &usage }
+if ($MAXLOAD =~ /^(\d+(\.\d+)?)$/) { $MAXLOAD = $1 } else { &usage }

 delete $ENV{ENV};
 $ENV{PATH} = '/bin:/usr/bin:/var/qmail/bin';
@@ -69,6 +74,32 @@
   exit(0);
 }

+my $curload = 0.0;
+sub getLoadAvg {
+       if ( $^O =~ /^linux$/i ) {
+               if ( open PROC_LOADAVG, '/proc/loadavg') {
+                       ($curload = <PROC_LOADAVG>) =~ 
s/^\s*(\d+\.\d+)\s.*$/$1/;
+                       close PROC_LOADAVG;
+               }
+               $curload =~ s/\n//;
+       }
+}
+my $ACCEPT_NEW_CONN = 1;
+my $running = 0;
+sub checkStatus {
+       if ( $MAXCONN > 0 ) {
+               $running = scalar keys %childstatus;
+               $ACCEPT_NEW_CONN = 0 if $running >= $MAXCONN;
+               $ACCEPT_NEW_CONN = 1 if $running < $MAXCONN;
+       }
+       if ( $MAXLOAD > 0.0 ) {
+               if ( $ACCEPT_NEW_CONN == 1 ) {
+                       getLoadAvg();
+                       $ACCEPT_NEW_CONN = 0 if $curload >= $MAXLOAD;
+               }
+       }
+}
+
 $SIG{CHLD} = \&REAPER;
 $SIG{INT} = \&HUNTSMAN;
 $SIG{TERM} = \&HUNTSMAN;
@@ -101,13 +132,25 @@
        ', group '.
        (getgrgid($)) || $)));

+if ( $MAXLOAD > 0.0 ) {
+       ::log(LOGINFO, "MAXLOAD detection enabled, will stop accepting 
connections once load avg reaches $MAXLOAD.");
+} else {
+       ::log(LOGINFO, "MAXLOAD detection disabled.");
+}
+if ( $MAXCONN > 0 ) {
+       ::log(LOGINFO, "MAXCONN detection enabled, will stop accepting 
connections once $MAXCONN connections is reached.");
+} else {
+       ::log(LOGINFO, "MAXCONN detection disabled.");
+}
+
 while (1) {
-  my $running = scalar keys %childstatus;
-  while ($running >= $MAXCONN) {
-    ::log(LOGINFO,"Too many connections: $running >= $MAXCONN.  Waiting one 
second.");
-    sleep(1) ;
     $running = scalar keys %childstatus;
-  }
+    checkStatus();
+    while ( $ACCEPT_NEW_CONN == 0 ) {
+        ::log(LOGINFO,"Either load or connections are too high.  Connections: 
$running/$MAXCONN Load: $curload/$MAXLOAD.  Waiting one second.");
+        sleep(1) ;
+       checkStatus();
+    }
     my $hisaddr = accept(my $client, $server);
     if (!$hisaddr) {
         # possible something condition...
@@ -167,7 +210,7 @@

     # don't do this!
     #$0 = "qpsmtpd-forkserver: $ENV{TCPREMOTEIP} / $ENV{TCPREMOTEHOST}";
-
+    $running++;
     ::log(LOGINFO, "Accepted connection $running/$MAXCONN from 
$ENV{TCPREMOTEIP} / $ENV{TCPREMOTEHOST}");

     # dup to STDIN/STDOUT
                   

********** End qp-fork-maxload.patch ***************    


Ed McLain
Sr. Colocation Engineer
TekLinks, Inc.

Reply via email to