]> git.pld-linux.org Git - packages/eximq.git/commitdiff
- initial; exim queue supervisor
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Fri, 15 Oct 2010 09:50:56 +0000 (09:50 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    eximq.args -> 1.1
    eximq.init -> 1.1
    eximq.pl -> 1.1
    eximq.spec -> 1.1

eximq.args [new file with mode: 0644]
eximq.init [new file with mode: 0644]
eximq.pl [new file with mode: 0644]
eximq.spec [new file with mode: 0644]

diff --git a/eximq.args b/eximq.args
new file mode 100644 (file)
index 0000000..f407b07
--- /dev/null
@@ -0,0 +1,20 @@
+# This file contains arguments for eximq instances
+#
+# Format:
+#   <agemin> <agemax> <interval> <processmax> [<exim_q_command>]
+
+
+# for exim3
+
+0s 1m 5s 10
+1m 2m 15s 10
+2m 5m 30s 10
+5m 15m 1m 5 /usr/sbin/exim -qq
+15m 2h 2m 5 /usr/sbin/exim -qq
+2h inf 5m 5 /usr/sbin/exim -qq
+
+# for exim4
+
+#5m 15m 15s 5 /usr/sbin/exim4 -qq
+#15m 2h 30s 5 /usr/sbin/exim4 -qq
+#2h inf 30s 5 /usr/sbin/exim4 -qq
diff --git a/eximq.init b/eximq.init
new file mode 100644 (file)
index 0000000..a07a0f0
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# eximqd       Start/Stop eximq supervisor
+#
+# chkconfig:   345 81 29
+# description: eximq - exim queue runners supervisor
+#
+# processname: eximq
+
+# Source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+EXIMQ_CONFIG=/etc/mail/eximq.args
+
+# Get service config
+[ -f /etc/sysconfig/exim ] && . /etc/sysconfig/exim
+[ -f /etc/sysconfig/eximq ] && . /etc/sysconfig/eximq
+
+if [ -n "$QUEUE" ]; then
+       echo "eximq requires QUEUE in /etc/sysconfig/exim to be empty" >&2
+       exit 1
+fi
+
+RETVAL=0
+# See how we were called.
+case "$1" in
+  start)
+       # Check if the service is already running?
+       if [ ! -f /var/lock/subsys/eximq ]; then
+               n=0
+               grep -v -e '^[[:space:]]*$' -e '^#' $EXIMQ_CONFIG | while read args; do
+                       n=$(printf '%02d' $(($n+1)))
+                       msg_starting "eximq $n daemon"
+                       daemon --user exim --makepid --pidfile /var/run/eximq.$n.pid --fork /usr/sbin/eximq.pl $args
+                       RETVAL=$?
+               done
+               [ $RETVAL -eq 0 ] && touch /var/lock/subsys/eximq
+       else
+               msg_already_running eximq
+       fi
+       ;;
+  stop)
+       # Stop daemons.
+       if [ -f /var/lock/subsys/eximq ]; then
+               n=0
+               grep -v -e '^[[:space:]]*$' -e '^#' $EXIMQ_CONFIG | while read args; do
+                       n=$(printf '%02d' $(($n+1)))
+                       msg_stopping "eximq $n daemon"
+                       killproc --pidfile /var/run/eximq.$n.pid eximq.pl
+               done
+               rm -f /var/lock/subsys/eximq >/dev/null 2>&1
+       else
+               msg_not_running eximq
+       fi
+       ;;
+  restart)
+       $0 stop
+       $0 start
+       exit $?
+       ;;
+  status)
+       status eximq.pl
+       exit $?
+       ;;
+  *)
+       msg_usage "$0 {start|stop|restart|status}"
+       exit 3
+esac
+
+exit $RETVAL
diff --git a/eximq.pl b/eximq.pl
new file mode 100644 (file)
index 0000000..21a11ce
--- /dev/null
+++ b/eximq.pl
@@ -0,0 +1,488 @@
+#!/usr/bin/perl -w
+
+## eximq
+##
+## (c) 2003-2004 Piotr Roszatycki <dexter@debian.org>, GPL
+##
+## $Id$
+
+=head1 NAME
+
+eximq - Supervising process for Exim's queue runners.
+
+=head1 SYNOPSIS
+
+B<eximq> B<-h>|B<--help>
+
+B<eximq> [B<--debug-stderr>] [B<--debug-syslog>]
+[B<--daemon>] S<[B<--pidfile> I<path>]>
+I<agemin> I<agemax> I<interval> I<processmax> [I<exim_q_command>]
+
+=cut
+
+use 5.006;
+use strict;
+
+use Getopt::Long qw(:config require_order no_auto_abbrev);
+use POSIX qw(:sys_wait_h :locale_h setsid);
+use Pod::Usage;
+use Unix::Syslog qw(:macros :subs);
+
+
+##############################################################################
+
+## Constant variables
+##
+
+## Program name
+my $NAME = "eximq";
+
+## Program version
+my $VERSION = 0.4;
+
+## Global variables
+##
+
+## Spawned command
+my @eximq_cmd = qw(/usr/sbin/exim -q);
+
+
+##############################################################################
+
+## Private variables
+##
+
+## Count for running spawned processes
+my $running = 0;
+
+## Process group ID
+my $pgrp = 0;
+
+## Getopt::Long handler
+my %opt = (
+    'pidfile' => "/var/run/$NAME.pid",
+);
+
+## Old value for LC_TIME
+my $old_lc_time = setlocale(LC_TIME);
+
+
+##############################################################################
+
+## debug($msg)
+##
+## Dumps message if debug mode is turned on.
+##
+sub debug(@) {
+    my (@msg) = @_;
+
+    if (${opt{'debug-syslog'}}) {
+        setlocale(LC_TIME, "C");
+        openlog($NAME, LOG_PID, LOG_MAIL);
+        syslog(LOG_INFO, "%s", join('', @msg));
+        closelog;
+        setlocale(LC_TIME, $old_lc_time);
+    }
+    if (${opt{'debug-stderr'}}) {
+        print STDERR "*** @msg\n";
+    }
+}
+
+
+## error($msg)
+##
+## Dumps error message
+##
+sub error(@) {
+    my (@msg) = @_;
+
+    setlocale(LC_TIME, "C");
+    openlog($NAME, LOG_PID, LOG_MAIL);
+    syslog(LOG_ERR, "%s", join('', @_));
+    closelog;
+    setlocale(LC_TIME, $old_lc_time);
+    print STDERR "*** @_\n";
+}
+
+
+## sig_handler($)
+##
+## Handler for process signal
+##
+sub sig_handler($) {
+    my($sig) = @_;
+    cleanup();
+    return unless defined $sig;
+    debug("Got a SIG$sig");
+    if ($pgrp) {
+        debug("Killing spawned processes for session $pgrp");
+        $SIG{'TERM'} = 'IGNORE';
+        kill 'TERM', -$pgrp;
+    }
+    die "Die for SIG$sig\n";
+}
+
+
+## daemonize()
+##
+## Daemonize main process
+##
+sub daemonize() {
+    chdir '/'               or die "Can't chdir to /: $!";
+    open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
+    open STDOUT, '>/dev/null'
+                            or die "Can't write to /dev/null: $!";
+    defined(my $pid = fork) or die "Can't fork: $!";
+    if ($pid) {
+        open PIDFILE, ">$opt{pidfile}" or die "Can't open pidfile $opt{pidfile}: $!";
+        print PIDFILE "$pid\n"         or die "Can't write pidfile $opt{pidfile}: $!";
+        close PIDFILE;
+        exit;
+    }
+    $pgrp = setsid or die "Can't start a new session: $!";
+    open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
+}
+
+
+## cleanup()
+##
+## Clean up before die
+##
+sub cleanup() {
+    unlink $opt{'pidfile'} if $opt{'daemon'} and -f $opt{'pidfile'};
+}
+
+
+## usleep($sec);
+##
+## Sleep $usec seconds (can be factorized)
+sub usleep($) {
+    my ($sec) = @_;
+
+    select(undef, undef, undef, $sec);
+}
+
+
+## $n62 = base62($n10);
+##
+## Convert decimal number to b62 string (part of Exim msgid)
+##
+sub base62($) {
+    my ($n10) = @_;
+
+    my $BASE = 62;
+    my @CHAR = qw(0 1 2 3 4 5 6 7 8 9
+                  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+                  a b c d e f g h i j k l m n o p q r s t u v w x y z);
+
+    my $n62 = "";
+
+    while ($n10 > 0) {
+        my $d = $n10 % $BASE;
+        $n62 = $CHAR[$d] . $n62;
+        $n10 = int $n10 / $BASE;
+    }
+
+    return $n62;
+}
+
+
+## $seconds = seconds($time);
+##
+## Converts time with modifiers [smhd] to seconds
+##
+sub seconds($) {
+    my ($time) = @_;
+
+    return $time if $time eq "inf";
+
+    my $seconds = 0;
+
+    my $modifier = "[smhd]";
+    while ($time =~ s/^([\d.]+)($modifier)//) {
+        if ($2 eq "s") { $seconds += $1;                $modifier = ""; }
+        if ($2 eq "m") { $seconds += $1 * 60;           $modifier = "s"; }
+        if ($2 eq "h") { $seconds += $1 * 60 * 60;      $modifier = "[sm]"; }
+        if ($2 eq "d") { $seconds += $1 * 60 * 60 * 24; $modifier = "[smh]"; }
+    }
+
+    return undef if $time ne "";
+
+    return $seconds;
+}
+
+
+## eximq_die(@msg)
+##
+## Die with message
+##
+sub eximq_die(@) {
+    my (@msg) = @_;
+
+    $SIG{'__DIE__'} = 'DEFAULT';
+    debug(@msg);
+    die(@msg, "\n");
+}
+
+
+## eximq_exec(@cmd)
+##
+## Execute command
+##
+sub eximq_exec(@) {
+    my (@cmd) = @_;
+
+    debug("exec " . join(' ', @cmd));
+    exec(@cmd);
+
+    die "Cannot exec: $!";
+}
+
+
+## eximq_spawn($msgid)
+##
+## Spawn neq eximq process
+##
+sub eximq_spawn(;$$) {
+    my ($msgid_min, $msgid_max) = @_;
+
+    my $pid;
+    if (!defined($pid = fork)) {
+        error("Cannot fork: $!");
+    } elsif ($pid) {
+        debug("spawn $pid");
+        $running++;
+    } else {
+        if (defined $msgid_max) {
+            eximq_exec(@eximq_cmd, "$msgid_min-000000-00", "$msgid_max-zzzzzz-zz");
+        } elsif (defined $msgid_min) {
+            eximq_exec(@eximq_cmd, "$msgid_min-000000-00");
+        } else {
+            eximq_exec(@eximq_cmd);
+        }
+    }
+}
+
+
+## eximq_reaper()
+##
+## Harvest spawned eximq processes
+##
+sub eximq_reaper() {
+
+    while ((my $pid = waitpid(-1,WNOHANG)) > 0) {
+        debug("reap $pid");
+        $running--;
+    }
+    $SIG{CHLD} = \&eximq_reaper;
+}
+
+
+## eximq($agemin, $agemax, $interval, $processmax);
+##
+## Start Exim queue processing
+##
+sub eximq($$$$) {
+    my ($agemin, $agemax, $interval, $processmax) = @_;
+    my $time_last = time;
+
+    $SIG{'__DIE__'} = \&eximq_die;
+    $SIG{$_} = 'IGNORE' foreach (qw(HUP PIPE USR1 USR2));
+    $SIG{$_} = \&sig_handler foreach (qw(INT QUIT TERM));
+    $SIG{'CHLD'} = \&eximq_reaper;
+
+    for (;;) {
+        if ($time_last + $interval < time && $running < $processmax) {
+            $time_last = time;
+            if ($agemax > 0 || $agemax eq "inf") {
+                my $msgid_min = "000000";
+                if ($agemax > 0) {
+                    $msgid_min .= base62($time_last - $agemax);
+                    $msgid_min =~ s/^.*(.{6})$/$1/;
+                }
+                if ($agemin > 0 || $agemin eq "inf") {
+                    my $msgid_max = "000000";
+                    if ($agemin > 0) {
+                        $msgid_max .= base62($time_last - $agemin);
+                        $msgid_max =~ s/^.*(.{6})$/$1/;
+                    }
+                    eximq_spawn($msgid_min, $msgid_max);
+                } else {
+                    eximq_spawn($msgid_min);
+                }
+            } else {
+                eximq_spawn();
+            }
+        }
+        sleep(1);
+    }
+}
+
+
+## main()
+##
+## Main subroutine
+##
+sub main() {
+
+    ## get options
+    my $opt = GetOptions(\%opt,
+        'help|h|?',
+        'debug-stderr',
+        'debug-syslog',
+        'daemon|d',
+        'pidfile|p=s',
+    );
+
+    pod2usage(2) unless $opt;
+
+    pod2usage(-verbose=>1, -message=>"$NAME $VERSION\n") if $opt{'help'};
+
+    pod2usage(2) if $#ARGV < 3;
+
+    ## set the process name
+    $0 = join(' ', $0, @ARGV);
+
+    my ($agemin, $agemax, $interval, $processmax);
+    defined ($agemin = seconds(shift @ARGV)) or die "Bad format for agemin argument\n";
+    defined ($agemax = seconds(shift @ARGV)) or die "Bad format for agemax argument\n";
+    ($agemin <= $agemax || $agemax eq "inf") or die "agemin argument cannot be greater than agemax argument\n";
+    defined ($interval = seconds(shift @ARGV)) or die "Bad format for interval argument\n";
+    ($processmax = shift @ARGV) =~ /^\d+$/ or die "Bad format for max argument\n";
+
+    if (@ARGV) {
+        @eximq_cmd = @ARGV;
+    }
+
+    daemonize() if $opt{'daemon'};
+    eximq($agemin, $agemax, $interval, $processmax);
+}
+
+
+END: {
+    cleanup();
+}
+
+
+main();
+
+
+__END__
+
+=head1 DESCRIPTION
+
+The B<eximq> controlls the count of queue runner processes based on
+messages age.  This allows to keep low system load and maximum number of
+queue runner processes.
+
+The B<eximq> can be started as daemon or foregound process.  The example
+init script is available as separate file and it requires F<eximq.args>
+file which contains arguments for each B<eximq> instances.
+
+The example F<eximq.args> file:
+
+ 0s 1m 5s 10
+ 1m 2m 15s 10
+ 2m 5m 30s 10
+ 5m 15m 1m 5 /usr/sbin/exim -qq
+ 15m 2h 2m 5 /usr/sbin/exim -qq
+ 2h inf 5m 5 /usr/sbin/exim -qq
+
+Also, the B<eximq> can be started from F</etc/inittab> file. I.e.:
+
+ # Exim queue
+ ex01:23:respawn:+/usr/sbin/eximq 0s 1m 5s 10
+ ex02:23:respawn:+/usr/sbin/eximq 1m 2m 15s 10
+ ex03:23:respawn:+/usr/sbin/eximq 2m 5m 30s 10
+ ex04:23:respawn:+/usr/sbin/eximq 5m 15m 1m 5 /usr/sbin/exim -qq
+ ex05:23:respawn:+/usr/sbin/eximq 15m 2h 2m 5 /usr/sbin/exim -qq
+ ex06:23:respawn:+/usr/sbin/eximq 2h inf 5m 5 /usr/sbin/exim -qq
+
+In this example the B<eximq> is started three times with different message
+ages.  The first process spawn max 10 queue runners each 5th second for
+messages not older than 1 minute.  The second process spawn max 10 runners
+each minute for messages not older than 5 minutes and nor newer than 1 minute.
+The last queue runners work for messages older than 2 hours and are spawned
+each 5th minute.
+
+The queue runner is spawned as B</usr/sbin/exim -q> command as default.
+Different command can be specified as last argument.
+
+For best result you should put
+
+ queue_only = yes
+ queue_smtp_domains = *
+
+in your F</etc/exim/exim.conf> configuration file.
+
+=head1 OPTIONS
+
+=over 8
+
+=item I<minage>
+
+Minimal age of message.  The queue runner will be process messages not newer
+than I<minage>.  B<0s> means no limits.
+
+=item I<maxage>
+
+Maximal age of message.  The queue runner will be process messages not older
+than I<maxage>.  B<inf> means no limits.  I<maxage> can not be lower than
+I<minage>.
+
+=item I<interval>
+
+Interval time between spawning another queue runner.  The new runner will be
+not spawned if the I<interval> time was not reached.
+
+=item I<processmax>
+
+Maximal number of spawned processes.  The new runner will be not spawned if
+whole number of running processes will be greater than I<processmax>.
+
+=item I<exim_q_command>
+
+The queue runner command.  The default is B</usr/sbin/exim -q>.
+
+=item B<--debug-stderr>
+
+Turn on debug mode on stderr.
+
+=item B<--debug-syslog>
+
+Turn on debug mode on syslog (MAIL|INFO).
+
+=item B<--daemon>
+
+Runs the B<eximq> as daemon. In this mode it creates pidfile and
+detaches from terminal.
+
+=item B<--pidfile> I<path>
+
+The I<path> for pidfile which is created if B<eximq> is started in
+daemon mode.  The default I<path> is F</var/run/eximq.pid>.  The file is
+removed after daemon dies.
+
+=item B<-h>|B<--help>
+
+This help.
+
+=back
+
+=head1 SIGNALS
+
+The B<eximq> ignores B<HUP>, B<PIPE>, B<USR1> and B<USR2> and dies for B<INT>,
+B<QUIT> and B<TERM>. It kills all spawned processes with B<TERM> after die
+signal.
+
+=head1 SEE ALSO
+
+B<exim>(8)
+
+=head1 AUTHOR
+
+(c) 2003-2004 Piotr Roszatycki E<lt>dexter@debian.orgE<gt>
+
+All rights reserved.  This program is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License, the
+latest version.
diff --git a/eximq.spec b/eximq.spec
new file mode 100644 (file)
index 0000000..914f391
--- /dev/null
@@ -0,0 +1,51 @@
+%include        /usr/lib/rpm/macros.perl
+Summary:       Supervising process for Exim's queue runners
+Name:          eximq
+Version:       0
+# date of eximq file edit
+Release:       0.20041227.1
+License:       GPL
+Group:         Applications/Mail
+# see homepage
+Source0:       %{name}.pl
+Source1:       %{name}.args
+Source2:       %{name}.init
+URL:           http://eximconf.alioth.debian.org/
+BuildRequires: rpm-perlprov >= 4.1-13
+Requires:      exim
+BuildArch:     noarch
+BuildRoot:     %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+
+%description
+
+%prep
+%setup -q -T -c
+
+%build
+
+%install
+rm -rf $RPM_BUILD_ROOT
+install -d $RPM_BUILD_ROOT{/etc/{rc.d/init.d,mail},%{_sbindir}}
+
+install %{SOURCE0} $RPM_BUILD_ROOT%{_sbindir}
+install %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/mail/eximq.args
+install %{SOURCE2} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name}
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add %{name}
+%service %{name} restart "%{name} daemon"
+
+%preun
+if [ "$1" = "0" ]; then
+       %service %{name} stop
+       /sbin/chkconfig --del %{name}
+fi
+
+%files
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_sbindir}/eximq.pl
+%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/mail/%{name}.args
+%attr(754,root,root) /etc/rc.d/init.d/%{name}
This page took 0.193481 seconds and 4 git commands to generate.