diff options
author | Bruce Guenter <bruce@untroubled.org> | 2016-01-20 09:20:07 -0600 |
---|---|---|
committer | Bruce Guenter <bruce@untroubled.org> | 2016-01-20 09:20:07 -0600 |
commit | a0d1b330ce00fa22ee469d03f50930f4a65b3243 (patch) | |
tree | 762bc86fc8583a43d15079fee1eeae9dd52984ff /lib | |
parent | a8261e028286a719b4ff4e81a2ce9e39ce4216ae (diff) | |
parent | 97f05128cdc483387e7826c4a9dd9f174910329c (diff) |
Merge branch 'bounce'
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/autoclose.h | 68 | ||||
-rw-r--r-- | lib/forkexec.cc | 171 | ||||
-rw-r--r-- | lib/forkexec.h | 41 | ||||
-rw-r--r-- | lib/makefield.cc | 17 | ||||
-rw-r--r-- | lib/makefield.h | 3 |
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__ |