Skip to content
Snippets Groups Projects
Commit 93dd95c4 authored by Brad Davidson's avatar Brad Davidson
Browse files

LMTP support, init scripts for Ubuntu and Debian, syslog, and much more.

parent e4c22c27
No related branches found
No related tags found
No related merge requests found
# Here you can specify your poolmon command line options.
#
#OPTIONS=""
#!/bin/bash
#
# /etc/rc.d/init.d/poolmon
#
# Starts the poolmon daemon
#
# description: Poolmon mailserver pool monitor
# processname: poolmon
# pidfile: /var/run/poolmon.pid
### BEGIN INIT INFO
# Provides: poolmon
# Required-Start: $network dovecot
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop Poolmon mailserver pool monitor
# Description: Poolmon monitors a pool of Dovecot director mailservers
# and dynamically alters their state based on active health
# checks performed at regular intervals.
### END INIT INFO
# Source function library.
#. /etc/init.d/functions
. /lib/lsb/init-functions
if [ -f /etc/default/poolmon -a $UID -eq 0 ]; then
. /etc/default/poolmon
fi
RETVAL=0
prog="Poolmon"
exec="/usr/sbin/poolmon"
pidfile="/var/run/poolmon.pid"
lockfile="/var/lock/poolmon"
start() {
[ $UID -eq 0 ] || exit 4
[ -x $exec ] || exit 5
echo -n $"Starting $prog: "
start_daemon -p $pidfile $exec $OPTIONS
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $lockfile
echo
}
stop() {
[ $UID -eq 0 ] || exit 4
echo -n $"Stopping $prog: "
killproc -p $pidfile $exec
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $lockfile
echo
}
reload() {
[ $UID -eq 0 ] || exit 4
echo -n $"Reloading $prog: "
killproc -p $pidfile $exec -HUP
RETVAL=$?
echo
}
#
# See how we were called.
#
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
force-reload|restart)
stop
sleep 1
start
RETVAL=$?
;;
condrestart|try-restart)
if [ -f $lockfile ]; then
stop
sleep 3
start
fi
;;
status)
status_of_proc -p $pidfile $exec
RETVAL=$?
;;
*)
echo $"Usage: $0 {condrestart|try-restart|start|stop|restart|reload|force-reload|status}"
RETVAL=2
[ "$1" = 'usage' ] && RETVAL=0
esac
exit $RETVAL
# Here you can specify your poolmon command line options.
#
[Service]
OPTIONS=""
[Unit]
Description=Poolmon monitors a pool of Dovecot director mailservers \
and dynamically alters their state based on active health \
checks performed at regular intervals.
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
EnvironmentFile=-/etc/default/poolmon
WorkingDirectory=/
ExecStart=/usr/sbin/poolmon -f $OPTIONS
Restart=on-failure
LimitNOFILE=10000
[Install]
WantedBy=multi-user.target
......@@ -26,9 +26,9 @@
use strict;
use Getopt::Long;
use IO::Socket::UNIX;
use IO::Socket::INET;
use IO::Socket::SSL;
use IO::Socket::IP;
use POSIX qw(setsid strftime);
use Sys::Syslog qw( :DEFAULT setlogsock);
$SIG{'PIPE'} = 'IGNORE';
......@@ -46,6 +46,8 @@ my $DIRECTOR = '/var/run/dovecot/director-admin';
my $CREDFILE = '';
my $USERNAME = '';
my $PASSWORD = '';
my $LMTP_FROM = '';
my $LMTP_TO = '';
my $WEIGHTS;
my %WEIGHTS;
......@@ -62,12 +64,21 @@ GetOptions('p|port=s' => \@PORTS,
'h|help|?' => \&help,
'w|weights=s' => \&opt_weights,
'c|credfile=s' => \&opt_credfile,
'lmtp-from=s' => \$LMTP_FROM,
'lmtp-to=s' => \$LMTP_TO,
) or help();
unless (@PORTS || @SSL_PORTS){
@PORTS = qw(110 143);
}
if (@SSL_PORTS) {
eval("use IO::Socket::SSL;");
if ($@) {
die "$@";
}
}
check_pidfile();
unless($NOFORK){
daemonize();
......@@ -99,6 +110,7 @@ Arguments:
-i, --interval=SECS Scan interval in seconds (default: 30)
-k, --lockfile=PATH PID/lockfile location (default: /var/run/poolmon.pid)
-l, --logfile=PATH Log file location (default: /var/log/poolmon.log)
Set to 'syslog' for writing to local syslog socket
-p, --port=PORT Port to check; repeat for multiple (default: 110, 143)
-s --ssl=PORT Port with SSL/TLS; repeat for multiple (default: none)
If the port is recognized as common IMAP or POP3 port,
......@@ -110,7 +122,9 @@ Arguments:
- Username on 1st line.
- Password on 2nd line.
(default: health checks will not attempt authentication)
-S, --socket=PATH Path to Dovecot director-admin socket
--lmtp-from=ADDR Sender e-mail address for LMTP
--lmtp-to=ADDR Recipient e-mail address for LMTP
-S, --socket=PATH Path to Dovecot director-admin socket
(default: /var/run/dovecot/director-admin)
-t, --timeout=SECS Port health check timeout in seconds (default: 5)
-w, --weights=PATH Location of file containing host:weight lines
......@@ -181,23 +195,35 @@ sub director_connect {
return $sock;
}
# Scan all ports on a given host
# Scan all ports on a given host. 1 == success. 0 == failure
sub scan_host {
my ($host, $weight) = @_;
my $OK = 1;
# Check non-SSL ports first
foreach my $port (@PORTS){
if (! scan_port($host, $port, 0)){
$OK = 0;
last;
sleep(3);
for (my $i=0; $i <= 3; $i++) {
if (! scan_port($host, $port, 0)){
$OK = 0;
last;
}
sleep(0.5);
}
}
}
# Skip SSL checks if standard checks indicated failure
return 0 unless $OK;
foreach my $port (@SSL_PORTS){
if (! scan_port($host, $port, 1)){
$OK = 0;
last;
sleep(3);
for (my $i=0; $i <= 3; $i++) {
if (! scan_port($host, $port, 1)){
$OK = 0;
last;
}
sleep(0.5);
}
}
}
return $OK;
......@@ -209,7 +235,7 @@ sub scan_port {
my $host = shift || return;
my $port = shift || return;
my $ssl = shift;
my $sock = $ssl ? IO::Socket::SSL : IO::Socket::INET;
my $sock = $ssl ? IO::Socket::SSL : IO::Socket::IP;
my $prot;
my $ret;
......@@ -229,6 +255,8 @@ sub scan_port {
return scan_imap($host, $port, $sock);
} elsif ($prot eq 'POP3'){
return scan_pop3($host, $port, $sock);
} elsif ($prot eq 'LMTP'){
return scan_lmtp($host, $port, $sock);
} elsif (readline($sock)){
$DEBUG && write_log("$host:$port Connection OK");
return 1
......@@ -310,6 +338,39 @@ sub scan_pop3 {
}
}
sub scan_lmtp {
my $host = shift || return;
my $port = shift || return;
my $sock = shift || return;
my $line = readline($sock);
if ($line =~ m/^2\d\d /){
if ($LMTP_TO){
printf $sock "MAIL FROM:<%s>\r\n", $LMTP_FROM;
my $mailreply = readline($sock);
if ($mailreply !~ m/^2\d\d /){
write_err("$host:$port LMTP Server Rejects sender address");
return 0;
}
printf $sock "RCPT TO:<%s>\r\n", $LMTP_TO;
my $mailreply = readline($sock);
if ($mailreply !~ m/^2\d\d /){
write_err("$host:$port LMTP Server Rejects recipient address");
return 0;
}
$DEBUG && write_log("$host:$port LMTP OK");
return 1;
} else {
$DEBUG && write_log("$host:$port LMTP Banner OK");
return 1;
}
} else {
$DEBUG && write_log("$host:$port LMTP Banner Check Failed");
return 0;
}
}
# Extract port and protocol from combined port command-line option
sub get_port_proto {
my $port = shift;
......@@ -320,6 +381,9 @@ sub get_port_proto {
} elsif ($port == 110 || $port == 995 || $port =~ m/POP3:(\d+)/i){
$port = $1 ? $1 : $port;
return $port, 'POP3';
} elsif ($port == 24 || $port =~ m/LMTP:(\d+)/i){
$port = $1 ? $1 : $port;
return $port, 'LMTP';
} else {
return $port, 'UNKNOWN';
}
......@@ -351,12 +415,31 @@ sub enable_host {
# Append a line to stdout
sub write_log {
printf(STDOUT "%s LOG %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_));
if ($LOGFILE eq "syslog") {
write_to_syslog("info", join('', @_));
} else {
printf(STDOUT "%s LOG %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_));
}
}
# Append a line to stderr
sub write_err {
printf(STDERR "%s ERR %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_));
if ($LOGFILE eq "syslog") {
write_to_syslog("err", join('', @_));
} else {
printf(STDERR "%s ERR %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_));
}
}
sub write_to_syslog {
my ($priority, $msg) = @_;
return 0 unless ($priority =~ /info|err|debug/);
setlogsock("unix");
openlog("poolmon", "pid,cons", "user");
syslog($priority, $msg);
closelog();
return 1;
}
# Check to see if the pidfile exists and the indicated pid is still running
......@@ -367,7 +450,7 @@ sub check_pidfile {
my $oldpid = readline($FH);
close($FH);
chomp $oldpid;
if (-d "/proc/$oldpid"){
if (kill(0, $oldpid)){
write_err("Already running as pid $oldpid");
exit(1);
}
......@@ -492,8 +575,10 @@ sub daemonize {
chdir('/') or die "Can't chdir to /: $!";
umask(0);
open(STDIN, '</dev/null') or die "Can't read /dev/null: $!";
open(STDOUT, ">>$LOGFILE") or die "Can't write to $LOGFILE: $!";
open(STDERR, ">>$LOGFILE") or die "Can't write to $LOGFILE: $!";
if ($LOGFILE ne "syslog") {
open(STDOUT, ">>$LOGFILE") or die "Can't write to $LOGFILE: $!";
open(STDERR, ">>$LOGFILE") or die "Can't write to $LOGFILE: $!";
}
defined(my $pid = fork()) or die "Can't fork: $!";
$pid && exit(0);
setsid() or die "Can't start a new session: $!";
......@@ -506,14 +591,17 @@ sub daemonize {
# Reopen logfiles and reparse weights on HUP
$SIG{'HUP'} = sub {
open(STDOUT, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!";
open(STDERR, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!";
if ($LOGFILE ne "syslog") {
open(STDOUT, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!";
open(STDERR, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!";
}
write_log("Logfile reopened");
load_weights();
load_credentials();
}
}
# Ensure pidfile cleanup on exit
END {
remove_pidfile();
}
description "Poolmon Dovecot Director Monitoring Daemon"
start on startup
stop on runlevel [016]
kill timeout 3
expect fork
respawn
exec poolmon -l syslog -c /etc/poolmon-credentials.conf
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment