summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBruce Guenter <bruce@untroubled.org>2016-01-20 09:20:07 -0600
committerBruce Guenter <bruce@untroubled.org>2016-01-20 09:20:07 -0600
commita0d1b330ce00fa22ee469d03f50930f4a65b3243 (patch)
tree762bc86fc8583a43d15079fee1eeae9dd52984ff /lib
parenta8261e028286a719b4ff4e81a2ce9e39ce4216ae (diff)
parent97f05128cdc483387e7826c4a9dd9f174910329c (diff)
Merge branch 'bounce'
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/autoclose.h68
-rw-r--r--lib/forkexec.cc171
-rw-r--r--lib/forkexec.h41
-rw-r--r--lib/makefield.cc17
-rw-r--r--lib/makefield.h3
6 files changed, 298 insertions, 3 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 2094f04..dba622d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -17,6 +17,7 @@ libmisc_a_SOURCES = \
itoa.h itoa.cc \
makefield.cc makefield.h \
netstring.h netstring.cc \
+ forkexec.cc forkexec.h \
selfpipe.cc selfpipe.h \
setenv.cc setenv.h
diff --git a/lib/autoclose.h b/lib/autoclose.h
new file mode 100644
index 0000000..8bd38fd
--- /dev/null
+++ b/lib/autoclose.h
@@ -0,0 +1,68 @@
+#ifndef NULLMAILER_AUTOCLOSE__H__
+#define NULLMAILER_AUTOCLOSE__H__
+
+#include <unistd.h>
+
+// Simple inline wrapper to automatically close an open file descriptor
+class autoclose
+{
+ private:
+ int fd;
+
+ public:
+ inline autoclose(int f = -1) : fd(f) { }
+ inline ~autoclose() { close(); }
+ inline operator int() const { return fd; }
+ inline int operator =(int f)
+ {
+ close();
+ return fd = f;
+ }
+ inline void close()
+ {
+ if (fd >= 0) {
+ ::close(fd);
+ fd = -1;
+ }
+ }
+};
+
+// Simple inline wrapper to handle opening and closing a pipe pair
+class autoclose_pipe
+{
+ private:
+ int fds[2];
+
+ public:
+ inline autoclose_pipe()
+ {
+ fds[0] = fds[1] = -1;
+ }
+ inline ~autoclose_pipe()
+ {
+ close();
+ }
+ inline int operator[](int i) const { return fds[i]; }
+ inline bool open()
+ {
+ return pipe(fds) == 0;
+ }
+ inline void close()
+ {
+ if (fds[0] >= 0) {
+ ::close(fds[0]);
+ ::close(fds[1]);
+ fds[0] = fds[1] = -1;
+ }
+ }
+ // Close one half of the pair, return the other, and mark both as if they were closed.
+ inline int extract(int which)
+ {
+ int result = fds[which];
+ ::close(fds[1-which]);
+ fds[0] = fds[1] = -1;
+ return result;
+ }
+};
+
+#endif // NULLMAILER_AUTOCLOSE__H__
diff --git a/lib/forkexec.cc b/lib/forkexec.cc
new file mode 100644
index 0000000..67a3842
--- /dev/null
+++ b/lib/forkexec.cc
@@ -0,0 +1,171 @@
+// nullmailer -- a simple relay-only MTA
+// Copyright (C) 2016 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 "config.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "cli++/cli++.h"
+#include "fdbuf/fdbuf.h"
+#include "mystring/mystring.h"
+#include "defines.h"
+#include "errcodes.h"
+#include "forkexec.h"
+
+#define ERR(MSG) do{ ferr << cli_program << ": " << MSG << ": " << strerror(errno) << endl; } while(0)
+#define FAIL(MSG) do{ ERR(MSG); return false; }while(0)
+
+static int fdnull = -1;
+
+fork_exec::fork_exec(const char* p)
+ : pid(-1), name(p)
+{
+}
+
+fork_exec::~fork_exec()
+{
+ wait();
+}
+
+bool fork_exec::operator!() const
+{
+ return pid < 0;
+}
+
+bool fork_exec::start(const char* args[], int redirn, int redirs[])
+{
+ autoclose_pipe pipes[redirn];
+ for (int i = 0; i < redirn; i++) {
+ if (redirs[i] == REDIRECT_PIPE_TO || redirs[i] == REDIRECT_PIPE_FROM)
+ if (!pipes[i].open())
+ FAIL("Could not create pipe");
+ if (redirs[i] == REDIRECT_NULL)
+ if (fdnull < 0)
+ if ((fdnull = open("/dev/null", O_RDWR)) < 0)
+ FAIL("Could not open \"/dev/null\"");
+ }
+ if ((pid = fork()) < 0)
+ FAIL("Could not fork");
+ if (pid == 0) {
+ // Child process, exec program
+ for (int i = 0; i < redirn; i++) {
+ int r = redirs[i];
+ if (r == REDIRECT_NULL)
+ dup2(fdnull, i);
+ else if (r == REDIRECT_PIPE_FROM) {
+ dup2(pipes[i][1], i);
+ pipes[i].close();
+ }
+ else if (r == REDIRECT_PIPE_TO) {
+ dup2(pipes[i][0], i);
+ pipes[i].close();
+ }
+ else if (r > 0) {
+ dup2(r, i);
+ if (r >= redirn)
+ close(r);
+ }
+ }
+ execv(args[0], (char**)args);
+ ERR("Could not exec " << name);
+ _exit(ERR_EXEC_FAILED);
+ }
+ for (int i = 0; i < redirn; i++) {
+ if (redirs[i] == REDIRECT_PIPE_TO)
+ redirs[i] = pipes[i].extract(1);
+ else if (redirs[i] == REDIRECT_PIPE_FROM)
+ redirs[i] = pipes[i].extract(0);
+ }
+ return true;
+}
+
+bool fork_exec::start(const char* program, int redirn, int redirs[])
+{
+ const char* args[2] = { program, NULL };
+ return start(args, redirn, redirs);
+}
+
+int fork_exec::wait_status()
+{
+ if (pid > 0) {
+ int status;
+ if (waitpid(pid, &status, 0) == pid) {
+ pid = -1;
+ return status;
+ }
+ }
+ return -1;
+}
+
+bool fork_exec::wait()
+{
+ if (pid > 0) {
+ int status = wait_status();
+ if (status < 0)
+ FAIL("Error catching the return value from " << name);
+ if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ if (status) {
+ ferr << cli_program << ": " << name << " failed: " << status << endl;
+ return false;
+ }
+ }
+ else
+ FAIL(name << " crashed or was killed");
+ }
+ return true;
+}
+
+mystring program_path(const char* basedir, const char* program, const char* envvar)
+{
+ if (envvar) {
+ const char* env;
+ if ((env = getenv(envvar)) != NULL)
+ return env;
+ }
+ mystring path = basedir;
+ path += '/';
+ path += program;
+ return path;
+}
+
+static const char* nqpath()
+{
+ static mystring cache;
+ if (!cache)
+ cache = program_path(SBIN_DIR, "nullmailer-queue", "NULLMAILER_QUEUE");
+ return cache.c_str();
+}
+
+queue_pipe::queue_pipe()
+ : fork_exec("nullmailer-queue")
+{
+}
+
+int queue_pipe::start()
+{
+ int redirs[] = { REDIRECT_PIPE_TO, REDIRECT_NULL, REDIRECT_NULL };
+ if (!fork_exec::start(nqpath(), 3, redirs))
+ return -1;
+ return redirs[0];
+}
diff --git a/lib/forkexec.h b/lib/forkexec.h
new file mode 100644
index 0000000..8857d93
--- /dev/null
+++ b/lib/forkexec.h
@@ -0,0 +1,41 @@
+#ifndef NULLMAILER__FORK_EXEC__H
+#define NULLMAILER__FORK_EXEC__H
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "mystring/mystring.h"
+#include "autoclose.h"
+
+mystring program_path(const char* basedir, const char* name, const char* envvar);
+
+#define REDIRECT_NONE -1
+#define REDIRECT_NULL -2
+#define REDIRECT_PIPE_FROM -3
+#define REDIRECT_PIPE_TO -4
+
+class fork_exec
+{
+ private:
+ pid_t pid;
+ const char* name;
+
+ public:
+ fork_exec(const char*);
+ ~fork_exec();
+ bool operator!() const;
+
+ bool start(const char* args[], int redirn, int redirs[]);
+ bool start(const char* program, int redirn, int redirs[]);
+ bool wait();
+ int wait_status();
+ inline void kill(int sig) { ::kill(pid, sig); }
+};
+
+class queue_pipe : public fork_exec
+{
+ public:
+ queue_pipe();
+ int start();
+};
+
+#endif
diff --git a/lib/makefield.cc b/lib/makefield.cc
index 69b7843..464842e 100644
--- a/lib/makefield.cc
+++ b/lib/makefield.cc
@@ -26,10 +26,11 @@
#include "itoa.h"
#include "mystring/mystring.h"
-mystring make_date()
+mystring make_date(time_t t)
{
char buf[256];
- time_t t = time(0);
+ if (t == 0)
+ t = time(0);
struct tm* l = localtime(&t);
strftime(buf, 256, "%a, %d %b %Y %H:%M:%S ", l);
#ifdef TM_HAS_GMTOFF
@@ -74,3 +75,15 @@ mystring make_messageid(const mystring& idhost)
tmp += '>';
return tmp;
}
+
+mystring make_boundary()
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ mystring tmp = itoa(tv.tv_sec);
+ tmp += '.';
+ tmp += itoa(tv.tv_usec, 6);
+ tmp += '.';
+ tmp += itoa(getpid());
+ return tmp;
+}
diff --git a/lib/makefield.h b/lib/makefield.h
index 1ac1169..9686ef2 100644
--- a/lib/makefield.h
+++ b/lib/makefield.h
@@ -1,7 +1,8 @@
#ifndef NULLMAILER__MAKEFIELD__H__
#define NULLMAILER__MAKEFIELD__H__
-extern mystring make_date();
+extern mystring make_date(time_t t = 0);
extern mystring make_messageid(const mystring& idhost);
+extern mystring make_boundary();
#endif // NULLMAILER__MAKEFIELD__H__