diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | doc/nullmailer-send.8 | 10 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/selfpipe.cc | 101 | ||||
-rw-r--r-- | lib/selfpipe.h | 16 | ||||
-rw-r--r-- | src/send.cc | 31 |
6 files changed, 161 insertions, 1 deletions
@@ -2,6 +2,9 @@ This file lists all the major user-visible changes to nullmailer. ------------------------------------------------------------------------------- Changes in version 1.04 +- Added a send timeout in nullmailer-send, to kill sending messages that + stall. + - Added support in nullmailer-inject for $NULLMAILER_QUEUE to name the program which is used to queue messages. diff --git a/doc/nullmailer-send.8 b/doc/nullmailer-send.8 index 01fc8a2..ac688d4 100644 --- a/doc/nullmailer-send.8 +++ b/doc/nullmailer-send.8 @@ -72,6 +72,16 @@ PLAIN method, use: .EE Blank lines and lines starting with a pound are ignored. +.TP +.B sendtimeout +The number of seconds to wait for a remote module listed above to +complete sending a message before killing it and trying again (defaults +to +.IR 3600 ). +If this is set to +.IR 0 , +.B nullmailer-send +will wait forever for messages to complete sending. .SH FILES .TP .B /var/nullmailer/queue diff --git a/lib/Makefile.am b/lib/Makefile.am index d39807a..102d9e5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,6 +16,7 @@ libmisc_a_SOURCES = \ itoa.h itoa.cc \ makefield.cc \ netstring.h netstring.cc \ + selfpipe.cc selfpipe.h \ setenv.cc setenv.h libnullmailer_a_SOURCES = diff --git a/lib/selfpipe.cc b/lib/selfpipe.cc new file mode 100644 index 0000000..e8e9cc1 --- /dev/null +++ b/lib/selfpipe.cc @@ -0,0 +1,101 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2007 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include "selfpipe.h" + +static int fds[2] = { -1, -1 }; + +static int fcntl_fl_on(int fd, int flag) +{ + int flags; + int newflags; + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + if ((newflags = flags | flag) != flags) + if (fcntl(fd, F_SETFL, newflags)) + return 0; + return 1; +} + +selfpipe::selfpipe() +{ + if (fds[0] < 0) { + if (pipe(fds) != -1) { + if (fcntl_fl_on(fds[0], O_NONBLOCK) + && fcntl_fl_on(fds[1], O_NONBLOCK) + && fcntl_fl_on(fds[1], FD_CLOEXEC) + && fcntl_fl_on(fds[1], FD_CLOEXEC)) + return; + + close(fds[0]); + close(fds[1]); + } + fds[0] = fds[1] = -1; + } +} + +selfpipe::operator bool() const +{ + return fds[0] >= 0; +} + +static void catcher(int sig) +{ + signal(sig, catcher); + write(fds[1], &sig, sizeof sig); +} + +void selfpipe::catchsig(int sig) +{ + signal(sig, catcher); +} + +int selfpipe::caught() +{ + int buf; + if (read(fds[0], &buf, sizeof buf) == sizeof buf) + return buf; + return 0; +} + +int selfpipe::waitsig(int timeout) +{ + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fds[0], &fdset); + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + int s; + while ((s = select(fds[0] + 1, &fdset, 0, 0, + (timeout <= 0) ? 0 : &tv)) == -1) { + if (errno != EINTR) + return -1; + } + if (s == 1) + return caught(); + return 0; +} diff --git a/lib/selfpipe.h b/lib/selfpipe.h new file mode 100644 index 0000000..1ad2136 --- /dev/null +++ b/lib/selfpipe.h @@ -0,0 +1,16 @@ +#ifndef NULLMAILER_SELFPIPE__H__ +#define NULLMAILER_SELFPIPE__H__ + +class selfpipe +{ + public: + selfpipe(); + + operator bool() const; + + void catchsig(int sig); + int caught(); + int waitsig(int timeout = 0); +}; + +#endif // NULLMAILER_SELFPIPE__H__ diff --git a/src/send.cc b/src/send.cc index bb1a8c8..b5eff57 100644 --- a/src/send.cc +++ b/src/send.cc @@ -1,5 +1,5 @@ // nullmailer -- a simple relay-only MTA -// Copyright (C) 2005 Bruce Guenter <bruce@untroubled.org> +// Copyright (C) 2007 Bruce Guenter <bruce@untroubled.org> // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -38,8 +38,11 @@ #include "hostname.h" #include "itoa.h" #include "list.h" +#include "selfpipe.h" #include "setenv.h" +selfpipe selfpipe; + typedef list<mystring> slist; #define fail(MSG) do { ferr << MSG << endl; return false; } while(0) @@ -98,6 +101,7 @@ unsigned ws_split(const mystring& str, slist& lst) static rlist remotes; static int pausetime = 60; +static int sendtimeout = 60*60; bool load_remotes() { @@ -126,6 +130,8 @@ bool load_config() if(!config_readint("pausetime", pausetime)) pausetime = 60; + if(!config_readint("sendtimeout", sendtimeout)) + sendtimeout = 60*60; return result; } @@ -180,6 +186,23 @@ void exec_protocol(int fd, remote& remote) bool catchsender(pid_t pid) { int status; + + for (;;) { + switch (selfpipe.waitsig(sendtimeout)) { + case 0: // timeout + kill(pid, SIGTERM); + waitpid(pid, &status, 0); + fail("Sending timed out, killing protocol"); + case -1: + fail("Error waiting for the child signal."); + case SIGCHLD: + break; + default: + continue; + } + break; + } + if(waitpid(pid, &status, 0) == -1) fail("Error catching the child process return value."); else { @@ -308,6 +331,12 @@ int main(int, char*[]) read_hostnames(); if (!config_read("helohost", hh)) hh = me; setenv("HELOHOST", hh.c_str(), 1); + + if(!selfpipe) { + fout << "Could not set up self-pipe." << endl; + return 1; + } + selfpipe.catchsig(SIGCHLD); if(!open_trigger()) return 1; |