├── README ├── debian ├── poolmon.default ├── poolmon.init ├── systemd.poolmon.default └── systemd.poolmon.service ├── poolmon ├── redhat ├── poolmon.init ├── poolmon.spec └── poolmon.sysconfig └── ubuntu └── poolmon.conf /README: -------------------------------------------------------------------------------- 1 | Poolmon is a director mailserver pool monitoring script for Dovecot, meant to 2 | roughly duplicate the functionality of node health monitors on dedicated load- 3 | balancers like LVS or F5 BigIP LTM. This script can be safely run on more than 4 | one director host simultaneously, although differences in node reachability may 5 | result in mailserver vhost count flapping. 6 | 7 | Consult the help text for more details on functionality. 8 | -------------------------------------------------------------------------------- /debian/poolmon.default: -------------------------------------------------------------------------------- 1 | # Here you can specify your poolmon command line options. 2 | # 3 | #OPTIONS="" 4 | -------------------------------------------------------------------------------- /debian/poolmon.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # /etc/rc.d/init.d/poolmon 4 | # 5 | # Starts the poolmon daemon 6 | # 7 | # description: Poolmon mailserver pool monitor 8 | # processname: poolmon 9 | # pidfile: /var/run/poolmon.pid 10 | 11 | ### BEGIN INIT INFO 12 | # Provides: poolmon 13 | # Required-Start: $network dovecot 14 | # Required-Stop: $network 15 | # Default-Start: 2 3 4 5 16 | # Default-Stop: 0 1 6 17 | # Short-Description: start and stop Poolmon mailserver pool monitor 18 | # Description: Poolmon monitors a pool of Dovecot director mailservers 19 | # and dynamically alters their state based on active health 20 | # checks performed at regular intervals. 21 | ### END INIT INFO 22 | 23 | # Source function library. 24 | #. /etc/init.d/functions 25 | . /lib/lsb/init-functions 26 | 27 | if [ -f /etc/default/poolmon -a $UID -eq 0 ]; then 28 | . /etc/default/poolmon 29 | fi 30 | 31 | RETVAL=0 32 | prog="Poolmon" 33 | exec="/usr/sbin/poolmon" 34 | pidfile="/var/run/poolmon.pid" 35 | lockfile="/var/lock/poolmon" 36 | 37 | start() { 38 | [ $UID -eq 0 ] || exit 4 39 | [ -x $exec ] || exit 5 40 | 41 | echo -n $"Starting $prog: " 42 | start_daemon -p $pidfile $exec $OPTIONS 43 | RETVAL=$? 44 | [ $RETVAL -eq 0 ] && touch $lockfile 45 | echo 46 | } 47 | 48 | stop() { 49 | [ $UID -eq 0 ] || exit 4 50 | echo -n $"Stopping $prog: " 51 | killproc -p $pidfile $exec 52 | RETVAL=$? 53 | [ $RETVAL -eq 0 ] && rm -f $lockfile 54 | echo 55 | } 56 | 57 | reload() { 58 | [ $UID -eq 0 ] || exit 4 59 | echo -n $"Reloading $prog: " 60 | killproc -p $pidfile $exec -HUP 61 | RETVAL=$? 62 | echo 63 | } 64 | 65 | # 66 | # See how we were called. 67 | # 68 | case "$1" in 69 | start) 70 | start 71 | ;; 72 | stop) 73 | stop 74 | ;; 75 | reload) 76 | reload 77 | ;; 78 | force-reload|restart) 79 | stop 80 | sleep 1 81 | start 82 | RETVAL=$? 83 | ;; 84 | condrestart|try-restart) 85 | if [ -f $lockfile ]; then 86 | stop 87 | sleep 3 88 | start 89 | fi 90 | ;; 91 | status) 92 | status_of_proc -p $pidfile $exec 93 | RETVAL=$? 94 | ;; 95 | *) 96 | echo $"Usage: $0 {condrestart|try-restart|start|stop|restart|reload|force-reload|status}" 97 | RETVAL=2 98 | [ "$1" = 'usage' ] && RETVAL=0 99 | esac 100 | 101 | exit $RETVAL 102 | 103 | -------------------------------------------------------------------------------- /debian/systemd.poolmon.default: -------------------------------------------------------------------------------- 1 | # Here you can specify your poolmon command line options. 2 | # 3 | [Service] 4 | OPTIONS="" 5 | -------------------------------------------------------------------------------- /debian/systemd.poolmon.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Poolmon monitors a pool of Dovecot director mailservers \ 3 | and dynamically alters their state based on active health \ 4 | checks performed at regular intervals. 5 | After=syslog.target network.target remote-fs.target nss-lookup.target 6 | 7 | [Service] 8 | Type=simple 9 | EnvironmentFile=-/etc/default/poolmon 10 | WorkingDirectory=/ 11 | ExecStart=/usr/sbin/poolmon -f $OPTIONS 12 | Restart=on-failure 13 | LimitNOFILE=10000 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | 18 | -------------------------------------------------------------------------------- /poolmon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # vim: ai ts=4 sts=4 et sw=4 3 | # 4 | # Director mailserver pool monitoring script, meant to roughly duplicate the 5 | # functionality of node health monitors on dedicated load-balancers like LVS or 6 | # F5 BigIP LTM. This script can be safely run on more than one director host 7 | # simultaneously, although differences in node reachability may result in 8 | # mailserver vhost count flapping. 9 | # 10 | # Consult the help text for more details on functionality. 11 | # 12 | # Brandon Davidson 13 | # 14 | ### License: 15 | # This program is free software; you can redistribute it and/or modify it 16 | # under the terms of the GNU General Public License as published by the 17 | # Free Software Foundation; either version 2, or (at your option) any 18 | # later version. 19 | # 20 | # This program is distributed in the hope that it will be useful, 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | # GNU General Public License for more details. 24 | ### 25 | 26 | use strict; 27 | use Getopt::Long; 28 | use IO::Socket::UNIX; 29 | use IO::Socket::INET6; 30 | use POSIX qw(setsid strftime); 31 | use Sys::Syslog qw( :DEFAULT setlogsock); 32 | 33 | $SIG{'PIPE'} = 'IGNORE'; 34 | 35 | my @PORTS; 36 | my @SSL_PORTS; 37 | my $DEBUG = 0; 38 | my $NOFORK = 0; 39 | my $TIMEOUT = 5; 40 | my $INTERVAL = 30; 41 | my $PIDOPEN = 0; 42 | my $ORIGPID = 0; 43 | my $PIDFILE = '/var/run/poolmon.pid'; 44 | my $LOGFILE = '/var/log/poolmon.log'; 45 | my $DIRECTOR = '/var/run/dovecot/director-admin'; 46 | my $CREDFILE = ''; 47 | my $USERNAME = ''; 48 | my $PASSWORD = ''; 49 | my $LMTP_FROM = ''; 50 | my $LMTP_TO = ''; 51 | 52 | Getopt::Long::Configure("bundling"); 53 | GetOptions('p|port=s' => \@PORTS, 54 | 's|ssl=s' => \@SSL_PORTS, 55 | 'd|debug' => \$DEBUG, 56 | 't|timeout=i' => \$TIMEOUT, 57 | 'l|logfile=s' => \$LOGFILE, 58 | 'k|lockfile=s' => \$PIDFILE, 59 | 'i|interval=i' => \$INTERVAL, 60 | 'f|foreground' => \$NOFORK, 61 | 'S|socket=s' => \$DIRECTOR, 62 | 'h|help|?' => \&help, 63 | 'c|credfile=s' => \&opt_credfile, 64 | 'lmtp-from=s' => \$LMTP_FROM, 65 | 'lmtp-to=s' => \$LMTP_TO, 66 | ) or help(); 67 | 68 | unless (@PORTS || @SSL_PORTS){ 69 | @PORTS = qw(110 143); 70 | } 71 | 72 | if (@SSL_PORTS) { 73 | eval("use IO::Socket::SSL;"); 74 | if ($@) { 75 | die "$@"; 76 | } 77 | } 78 | 79 | check_pidfile(); 80 | unless($NOFORK){ 81 | daemonize(); 82 | } 83 | 84 | while(1){ 85 | scan_host_loop(); 86 | sleep($INTERVAL); 87 | } 88 | exit(0); 89 | 90 | ### It's subroutines, all the way down. 91 | 92 | # Print help text and exit 93 | sub help { 94 | print "Usage: $0 [] 95 | Retrieves a set of mailserver hosts from a Dovecot director and performs 96 | parallel checks against a list of ports. 97 | 98 | If any port on a host cannot be connected to or fails to present a banner line 99 | within the timeout period, that host is disabled by setting its status to 100 | 'down', and flushing active mappings from that host. If a disabled host passes 101 | all host checks, its status is restored to 'up'. 102 | 103 | Arguments: 104 | -d, --debug Show additional logging (default: false) 105 | -f, --foreground Run in foreground (default: false) 106 | -h, --help This text 107 | -i, --interval=SECS Scan interval in seconds (default: 30) 108 | -k, --lockfile=PATH PID/lockfile location (default: /var/run/poolmon.pid) 109 | -l, --logfile=PATH Log file location (default: /var/log/poolmon.log) 110 | Set to 'syslog' for writing to local syslog socket 111 | -p, --port=PORT Port to check; repeat for multiple (default: 110, 143) 112 | -s --ssl=PORT Port with SSL/TLS; repeat for multiple (default: none) 113 | If the port is recognized as common IMAP or POP3 port, 114 | health checks will look for a valid connect banner. 115 | You can force a protocol (IMAP or POP3) by specifying 116 | it before the port, separated by a colon. Example: 117 | --port POP3:110 --ssl IMAP:993 118 | -c --credfile=PATH File with credentials to authenticate as, mode 0600. 119 | - Username on 1st line. 120 | - Password on 2nd line. 121 | (default: health checks will not attempt authentication) 122 | --lmtp-from=ADDR Sender e-mail address for LMTP 123 | --lmtp-to=ADDR Recipient e-mail address for LMTP 124 | -S, --socket=PATH Path to Dovecot director-admin socket 125 | (default: /var/run/dovecot/director-admin) 126 | -t, --timeout=SECS Port health check timeout in seconds (default: 5) 127 | "; 128 | exit(1); 129 | } 130 | 131 | # Get list of mailservers from local director and scan them 132 | sub scan_host_loop { 133 | # Get mailserver list 134 | my $sock = director_connect() || return; 135 | my @lines; 136 | my %CHILDREN; 137 | print $sock "HOST-LIST\n"; 138 | while(my $line = readline($sock)){ 139 | last if $line eq "\n"; 140 | chomp $line; 141 | push(@lines, $line); 142 | } 143 | close($sock); 144 | undef($sock); 145 | $DEBUG && write_log('Director returned ',scalar(@lines),' hosts'); 146 | 147 | # Start host scans 148 | foreach my $line (@lines){ 149 | if(my ($host, $weight, $clients, $tag, $updown) = split("\t", $line)){ 150 | my $pid = fork(); 151 | if ($pid == 0) { 152 | exit(scan_host($host, $updown)); 153 | } elsif ($pid > 0) { 154 | $CHILDREN{$pid} = [$host, $updown]; 155 | } else { 156 | write_err("Failed to fork scan process for host $host: $!"); 157 | } 158 | } 159 | } 160 | 161 | # Take action based on check results returned by subprocesses 162 | while(my $pid = wait()){ 163 | my $OK = $? >> 8; 164 | last if $pid == -1; 165 | my ($host, $updown) = @{$CHILDREN{$pid}}; 166 | $DEBUG && write_log("Scan of host $host with status $updown by PID $pid exited with status $OK"); 167 | if ($OK) { # Enable host if currently disabled (updown=D) 168 | enable_host($host) if $updown eq "D"; 169 | } else { # Disable host if currently enabled (updown=U) 170 | disable_host($host) if $updown eq "U"; 171 | } 172 | undef($CHILDREN{$pid}); 173 | } 174 | $DEBUG && write_log('Host scans complete'); 175 | } 176 | 177 | # Connect to local director and perform version handshake 178 | sub director_connect { 179 | my $HANDSHAKE = "VERSION\tdirector-doveadm\t1\t0\n"; 180 | my $sock = new IO::Socket::UNIX($DIRECTOR); 181 | if ($sock) { 182 | print $sock $HANDSHAKE; 183 | unless (readline($sock) eq $HANDSHAKE){ 184 | write_err("Director handshake failed!"); 185 | close($sock); 186 | } 187 | } else { 188 | write_err("Failed to connect to director: $!"); 189 | } 190 | return $sock; 191 | } 192 | 193 | # Scan all ports on a given host. 1 == success. 0 == failure 194 | sub scan_host { 195 | my ($host, $updown) = @_; 196 | my $OK = 1; 197 | # Check non-SSL ports first 198 | foreach my $port (@PORTS){ 199 | if (! scan_port($host, $port, 0)){ 200 | sleep(3); 201 | for (my $i=0; $i <= 3; $i++) { 202 | if (! scan_port($host, $port, 0)){ 203 | $OK = 0; 204 | last; 205 | } 206 | sleep(0.5); 207 | } 208 | } 209 | } 210 | # Skip SSL checks if standard checks indicated failure 211 | return 0 unless $OK; 212 | foreach my $port (@SSL_PORTS){ 213 | if (! scan_port($host, $port, 1)){ 214 | sleep(3); 215 | for (my $i=0; $i <= 3; $i++) { 216 | if (! scan_port($host, $port, 1)){ 217 | $OK = 0; 218 | last; 219 | } 220 | sleep(0.5); 221 | } 222 | } 223 | } 224 | return $OK; 225 | } 226 | 227 | # Check a port on a host, with optional SSL and login 228 | sub scan_port { 229 | no strict 'subs'; 230 | my $host = shift || return; 231 | my $port = shift || return; 232 | my $ssl = shift; 233 | my $sock = $ssl ? IO::Socket::SSL : IO::Socket::INET6; 234 | my $prot; 235 | my $ret; 236 | 237 | ($port, $prot) = get_port_proto($port); 238 | $sock = $sock->new(PeerAddr => $host, 239 | PeerPort => $port, 240 | Timeout => $TIMEOUT); 241 | 242 | $ret = eval { 243 | local $SIG{ALRM} = sub { 244 | $DEBUG && write_log("Timeout"); 245 | die "Timeout\n"; 246 | }; 247 | alarm $TIMEOUT; 248 | if ($sock){ 249 | if ($prot eq 'IMAP'){ 250 | return scan_imap($host, $port, $sock); 251 | } elsif ($prot eq 'POP3'){ 252 | return scan_pop3($host, $port, $sock); 253 | } elsif ($prot eq 'LMTP'){ 254 | return scan_lmtp($host, $port, $sock); 255 | } elsif (readline($sock)){ 256 | $DEBUG && write_log("$host:$port Connection OK"); 257 | return 1 258 | } 259 | } else { 260 | $DEBUG && write_log("$host:$port Connection Failed"); 261 | return 0; 262 | } 263 | alarm 0; 264 | }; 265 | 266 | if ($@){ 267 | $DEBUG && write_log("$host:$port Read Timeout"); 268 | } 269 | 270 | return $ret; 271 | } 272 | 273 | sub scan_imap { 274 | my $host = shift || return; 275 | my $port = shift || return; 276 | my $sock = shift || return; 277 | my $line = readline($sock); 278 | if ($line =~ m/LOGINDISABLED/){ 279 | write_err("$host:$port IMAP Server Reports Plaintext Login Disabled - check login_trusted_networks"); 280 | return 1; 281 | } elsif ($line =~ m/^\* OK/){ 282 | if ($USERNAME && $PASSWORD){ 283 | printf $sock "01 LOGIN %s {%d}\r\n%s\r\n", $USERNAME, length($PASSWORD), $PASSWORD; 284 | my $literal = readline($sock); 285 | my $logstat = readline($sock); 286 | if ($literal =~ m/^\+ OK/ && $logstat =~ m/^01 OK/){ 287 | $DEBUG && write_log("$host:$port IMAP Login OK"); 288 | return 1; 289 | } else { 290 | $DEBUG && write_log("$host:$port IMAP Login Failed"); 291 | $DEBUG && write_log("\t\t$logstat"); 292 | return 0; 293 | } 294 | } else { 295 | printf $sock "01 LOGOUT\r\n"; 296 | $DEBUG && write_log("$host:$port IMAP Banner OK"); 297 | return 1; 298 | } 299 | } else { 300 | $DEBUG && write_log("$host:$port IMAP Banner Check Failed"); 301 | return 0; 302 | } 303 | } 304 | 305 | sub scan_pop3 { 306 | my $host = shift || return; 307 | my $port = shift || return; 308 | my $sock = shift || return; 309 | my $line = readline($sock); 310 | if ($line =~ m/\+OK/){ 311 | if ($USERNAME && $PASSWORD){ 312 | printf $sock "USER %s\r\nPASS %s\r\n", $USERNAME, $PASSWORD; 313 | my $userval = readline($sock); 314 | my $logstat = readline($sock); 315 | if ($userval =~ m/^-ERR Plaintext authentication disallowed/){ 316 | write_err("$host:$port POP3 Server Reports Plaintext Login Disabled - check login_trusted_networks"); 317 | return 1; 318 | } elsif ($userval =~ m/^\+OK/ && $logstat =~ m/^\+OK/){ 319 | $DEBUG && write_log("$host:$port POP3 Login OK"); 320 | return 1; 321 | } else { 322 | $DEBUG && write_log("$host:$port POP3 Login Failed"); 323 | $DEBUG && write_log("\t\t$logstat"); 324 | return 0; 325 | } 326 | } else { 327 | $DEBUG && write_log("$host:$port POP3 Banner OK"); 328 | return 1; 329 | } 330 | } else { 331 | $DEBUG && write_log("$host:$port POP3 Banner Check Failed"); 332 | return 0; 333 | } 334 | } 335 | 336 | sub scan_lmtp { 337 | my $host = shift || return; 338 | my $port = shift || return; 339 | my $sock = shift || return; 340 | my $line = readline($sock); 341 | if ($line =~ m/^2\d\d /){ 342 | if ($LMTP_TO){ 343 | printf $sock "MAIL FROM:<%s>\r\n", $LMTP_FROM; 344 | my $mailreply = readline($sock); 345 | if ($mailreply !~ m/^2\d\d /){ 346 | write_err("$host:$port LMTP Server Rejects sender address"); 347 | return 0; 348 | } 349 | 350 | printf $sock "RCPT TO:<%s>\r\n", $LMTP_TO; 351 | my $mailreply = readline($sock); 352 | if ($mailreply !~ m/^2\d\d /){ 353 | write_err("$host:$port LMTP Server Rejects recipient address"); 354 | return 0; 355 | } 356 | 357 | $DEBUG && write_log("$host:$port LMTP OK"); 358 | return 1; 359 | } else { 360 | $DEBUG && write_log("$host:$port LMTP Banner OK"); 361 | return 1; 362 | } 363 | } else { 364 | $DEBUG && write_log("$host:$port LMTP Banner Check Failed"); 365 | return 0; 366 | } 367 | } 368 | 369 | # Extract port and protocol from combined port command-line option 370 | sub get_port_proto { 371 | my $port = shift; 372 | 373 | if ($port == 143 || $port == 993 || $port =~ m/IMAP:(\d+)/i){ 374 | $port = $1 ? $1 : $port; 375 | return $port, 'IMAP'; 376 | } elsif ($port == 110 || $port == 995 || $port =~ m/POP3:(\d+)/i){ 377 | $port = $1 ? $1 : $port; 378 | return $port, 'POP3'; 379 | } elsif ($port == 24 || $port =~ m/LMTP:(\d+)/i){ 380 | $port = $1 ? $1 : $port; 381 | return $port, 'LMTP'; 382 | } else { 383 | return $port, 'UNKNOWN'; 384 | } 385 | } 386 | 387 | # Mark host down and flush associations 388 | # Note that there's no way to kill active sessions, they'll have to time out 389 | # on their own. 390 | sub disable_host { 391 | my $host = shift || return; 392 | my $sock = director_connect() || return; 393 | print $sock "HOST-DOWN\t$host\n"; 394 | print $sock "HOST-FLUSH\t$host\n"; 395 | close($sock); 396 | undef($sock); 397 | write_log("$host disabled"); 398 | } 399 | 400 | # Mark host as up 401 | sub enable_host { 402 | my $host = shift || return; 403 | my $sock = director_connect() || return; 404 | print $sock "HOST-UP\t$host\n"; 405 | close($sock); 406 | undef($sock); 407 | write_log("$host enabled"); 408 | } 409 | 410 | # Append a line to stdout 411 | sub write_log { 412 | if ($LOGFILE eq "syslog") { 413 | write_to_syslog("info", join('', @_)); 414 | } else { 415 | printf(STDOUT "%s LOG %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_)); 416 | } 417 | } 418 | 419 | # Append a line to stderr 420 | sub write_err { 421 | if ($LOGFILE eq "syslog") { 422 | write_to_syslog("err", join('', @_)); 423 | } else { 424 | printf(STDERR "%s ERR %s\n", strftime("%h %d %H:%M:%S", localtime()), join('', @_)); 425 | } 426 | } 427 | 428 | sub write_to_syslog { 429 | my ($priority, $msg) = @_; 430 | return 0 unless ($priority =~ /info|err|debug/); 431 | 432 | setlogsock("unix"); 433 | openlog("poolmon", "pid,cons", "user"); 434 | syslog($priority, $msg); 435 | closelog(); 436 | return 1; 437 | } 438 | 439 | # Check to see if the pidfile exists and the indicated pid is still running 440 | sub check_pidfile { 441 | if (-e $PIDFILE){ 442 | my $FH; 443 | open($FH, "<$PIDFILE"); 444 | my $oldpid = readline($FH); 445 | close($FH); 446 | chomp $oldpid; 447 | if (kill(0, $oldpid)){ 448 | write_err("Already running as pid $oldpid"); 449 | exit(1); 450 | } 451 | } 452 | } 453 | 454 | # Remove the pidfile and exit 455 | sub remove_pidfile { 456 | return unless $PIDOPEN; 457 | if ($$ == $ORIGPID) { 458 | write_log('Shutting down'); 459 | unlink($PIDFILE); 460 | } 461 | } 462 | 463 | sub opt_credfile { 464 | my ($opt, $value)= @_; 465 | if ( -e $value ){ 466 | $CREDFILE = $value; 467 | load_credentials(); 468 | } else { 469 | write_err("Credential file '$value' not found!"); 470 | exit(1); 471 | } 472 | } 473 | 474 | sub load_credentials { 475 | return unless $CREDFILE; 476 | # Check permissions on credentials file 477 | if ((stat($CREDFILE))[2] & 077){ 478 | write_err("You should really chmod 600 $CREDFILE"); 479 | } 480 | if(open(my $credfh, "<$CREDFILE")){ 481 | $USERNAME = readline($credfh); 482 | $PASSWORD = readline($credfh); 483 | close($credfh); 484 | chomp($USERNAME); 485 | chomp($PASSWORD); 486 | if ($USERNAME && $PASSWORD){ 487 | $DEBUG && write_log("Loaded credentials from $CREDFILE"); 488 | } else { 489 | write_err("Could not read username and password from $CREDFILE"); 490 | } 491 | } else { 492 | write_err("Could not open credential file: $!"); 493 | } 494 | } 495 | 496 | 497 | 498 | # Fork into background and write pidfile 499 | sub daemonize { 500 | my $FH; 501 | 502 | # Open pidfile and register cleanup handlers 503 | # Do this before forking so we can abort on failure 504 | $PIDOPEN = open($FH, ">$PIDFILE") or die "Can't open pidfile $PIDFILE: $!"; 505 | $SIG{'INT'} = sub { 506 | $DEBUG && write_log("Caught signal INT"); 507 | remove_pidfile(); 508 | }; 509 | $SIG{'QUIT'} = sub { 510 | $DEBUG && write_log("Caught signal QUIT"); 511 | remove_pidfile(); 512 | }; 513 | $SIG{'TERM'} = sub { 514 | $DEBUG && write_log("Caught signal TERM"); 515 | remove_pidfile(); 516 | }; 517 | 518 | # Disconnect from terminal and session 519 | chdir('/') or die "Can't chdir to /: $!"; 520 | umask(0); 521 | open(STDIN, '>$LOGFILE") or die "Can't write to $LOGFILE: $!"; 524 | open(STDERR, ">>$LOGFILE") or die "Can't write to $LOGFILE: $!"; 525 | } 526 | defined(my $pid = fork()) or die "Can't fork: $!"; 527 | $pid && exit(0); 528 | setsid() or die "Can't start a new session: $!"; 529 | 530 | # Write forked PID to pidfile and startup message to log 531 | write_log("Forked to background as PID $$"); 532 | print $FH "$$\n"; 533 | close($FH); 534 | $ORIGPID = $$; 535 | 536 | # Reopen logfiles on HUP 537 | $SIG{'HUP'} = sub { 538 | if ($LOGFILE ne "syslog") { 539 | open(STDOUT, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!"; 540 | open(STDERR, ">>$LOGFILE") or die "Can't reopen $LOGFILE: $!"; 541 | } 542 | write_log("Logfile reopened"); 543 | load_credentials(); 544 | } 545 | } 546 | 547 | # Ensure pidfile cleanup on exit 548 | END { 549 | remove_pidfile(); 550 | } 551 | -------------------------------------------------------------------------------- /redhat/poolmon.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # /etc/rc.d/init.d/poolmon 4 | # 5 | # Starts the poolmon daemon 6 | # 7 | # chkconfig: - 70 30 8 | # description: Poolmon mailserver pool monitor 9 | # processname: poolmon 10 | # pidfile: /var/run/poolmon.pid 11 | 12 | ### BEGIN INIT INFO 13 | # Provides: poolmon 14 | # Required-Start: $network 15 | # Required-Stop: $network 16 | # Default-Start: 17 | # Default-Stop: 0 1 2 3 4 5 6 18 | # Short-Description: start and stop Poolmon mailserver pool monitor 19 | # Description: Poolmon monitors a pool of Dovecot director mailservers 20 | # and dynamically alters their state based on active health 21 | # checks performed at regular intervals. 22 | ### END INIT INFO 23 | 24 | # Source function library. 25 | . /etc/init.d/functions 26 | 27 | if [ -f /etc/sysconfig/poolmon -a $UID -eq 0 ]; then 28 | . /etc/sysconfig/poolmon 29 | fi 30 | 31 | RETVAL=0 32 | prog="Poolmon" 33 | exec="/usr/sbin/poolmon" 34 | pidfile="/var/run/poolmon.pid" 35 | lockfile="/var/lock/subsys/poolmon" 36 | 37 | start() { 38 | [ $UID -eq 0 ] || exit 4 39 | [ -x $exec ] || exit 5 40 | 41 | echo -n $"Starting $prog: " 42 | daemon --pidfile $pidfile $exec $OPTIONS 43 | RETVAL=$? 44 | [ $RETVAL -eq 0 ] && touch $lockfile 45 | echo 46 | } 47 | 48 | stop() { 49 | [ $UID -eq 0 ] || exit 4 50 | echo -n $"Stopping $prog: " 51 | killproc -p $pidfile $exec 52 | RETVAL=$? 53 | [ $RETVAL -eq 0 ] && rm -f $lockfile 54 | echo 55 | } 56 | 57 | reload() { 58 | [ $UID -eq 0 ] || exit 4 59 | echo -n $"Reloading $prog: " 60 | killproc -p $pidfile $exec -HUP 61 | RETVAL=$? 62 | echo 63 | } 64 | 65 | # 66 | # See how we were called. 67 | # 68 | case "$1" in 69 | start) 70 | start 71 | ;; 72 | stop) 73 | stop 74 | ;; 75 | reload) 76 | reload 77 | ;; 78 | force-reload|restart) 79 | stop 80 | sleep 1 81 | start 82 | RETVAL=$? 83 | ;; 84 | condrestart|try-restart) 85 | if [ -f $lockfile ]; then 86 | stop 87 | sleep 3 88 | start 89 | fi 90 | ;; 91 | status) 92 | status -p $pidfile $exec 93 | RETVAL=$? 94 | ;; 95 | *) 96 | echo $"Usage: $0 {condrestart|try-restart|start|stop|restart|reload|force-reload|status}" 97 | RETVAL=2 98 | [ "$1" = 'usage' ] && RETVAL=0 99 | esac 100 | 101 | exit $RETVAL 102 | 103 | -------------------------------------------------------------------------------- /redhat/poolmon.spec: -------------------------------------------------------------------------------- 1 | Name: poolmon 2 | Version: 0.6 3 | Release: 1%{?dist} 4 | Summary: poolmon is a director mailserver pool monitoring script for Dovecot 5 | 6 | Group: Applications/Publishing 7 | License: GPLv2+ 8 | URL: http://github.com/brandond/poolmon 9 | Source0: http://github.com/brandond/%{name}/raw/%{version}/poolmon 10 | Source1: http://github.com/brandond/%{name}/raw/%{version}/redhat/poolmon.init 11 | Source2: http://github.com/brandond/%{name}/raw/%{version}/redhat/poolmon.sysconfig 12 | 13 | BuildArch: noarch 14 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 15 | Requires: dovecot 16 | Requires: perl(IO::Socket::SSL) 17 | Requires: perl(IO::Socket::INET6) 18 | 19 | %description 20 | Poolmon is a director mailserver pool monitoring script for Dovecot, meant to 21 | roughly duplicate the functionality of node health monitors on dedicated load- 22 | balancers like Linux LVS or F5 BigIP hardware. 23 | 24 | %prep 25 | 26 | %build 27 | 28 | %install 29 | rm -rf %{buildroot} 30 | 31 | chmod a+x %{SOURCE0} %{SOURCE1} 32 | 33 | mkdir -p %{buildroot}%{_sbindir} 34 | mkdir -p %{buildroot}%{_sysconfdir}/rc.d/init.d/ 35 | mkdir -p %{buildroot}%{_sysconfdir}/sysconfig/ 36 | 37 | cp %{SOURCE0} %{buildroot}%{_sbindir} 38 | cp %{SOURCE1} %{buildroot}%{_sysconfdir}/rc.d/init.d/%{name} 39 | cp %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name} 40 | 41 | %clean 42 | rm -rf %{buildroot} 43 | 44 | %post 45 | /sbin/chkconfig --add %{name} 46 | 47 | %preun 48 | if [ "$1" -eq "0" ]; then 49 | /sbin/service %{name} stop > /dev/null 2>&1 50 | /sbin/chkconfig --del %{name} 51 | fi 52 | 53 | %postun 54 | if [ "$1" -ge "1" ]; then 55 | /sbin/service %{name} condrestart >/dev/null 2>&1 || : 56 | fi 57 | 58 | %files 59 | %defattr(-,root,root,-) 60 | %{_sbindir}/%{name} 61 | %{_sysconfdir}/rc.d/init.d/%{name} 62 | %config %{_sysconfdir}/sysconfig/%{name} 63 | 64 | %changelog 65 | * Tue Jul 2 2012 Brandon Davidson 66 | - Updated version to 0.5 with redone simple login checks, including support for 67 | POP3 authentication. 68 | * Thu Jun 21 2012 Brandon Davidson 69 | - Updated version to 0.4 with support for login checks by dfwarden@gmail.com 70 | * Thu Aug 19 2010 Brandon Davidson 71 | - Initial Package 72 | 73 | -------------------------------------------------------------------------------- /redhat/poolmon.sysconfig: -------------------------------------------------------------------------------- 1 | # Here you can specify your poolmon command line options. 2 | # 3 | #OPTIONS="" 4 | -------------------------------------------------------------------------------- /ubuntu/poolmon.conf: -------------------------------------------------------------------------------- 1 | description "Poolmon Dovecot Director Monitoring Daemon" 2 | 3 | start on startup 4 | stop on runlevel [016] 5 | 6 | kill timeout 3 7 | expect fork 8 | 9 | respawn 10 | 11 | exec poolmon -l syslog -c /etc/poolmon-credentials.conf 12 | --------------------------------------------------------------------------------