From: epg <> Date: Tue, 20 Aug 2002 05:07:37 +0000 (+0000) Subject: New program. X-Git-Url: https://diplodocus.org/git/mdeliver/commitdiff_plain/256a84cc849c0fc27a3547c13b23873685425360 New program. --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2316fcb --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +# $Id$ + +PROG= mdeliver +NOMAN= #defined + +CFLAGS= -Wall + +prefix?= /usr/local +exec_prefix?= ${prefix} +BINDIR?= ${exec_prefix}/bin +MANDIR?= ${prefix}/man + +.include diff --git a/mdeliver.c b/mdeliver.c new file mode 100644 index 0000000..d65214b --- /dev/null +++ b/mdeliver.c @@ -0,0 +1,158 @@ +/* mdeliver.c -- Deliver a message to a Maildir. + * + * $Id$ + * + * This program is in the public domain. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEN_ULONG 40 /* holds 2^128 - 1 in decimal, plus '\0' */ + +/* Write all of buf, even if write(2) is interrupted. */ +static ssize_t +full_write(int fd, const char *buf, size_t nbytes) +{ + ssize_t r, w = 0; + + /* Loop until nbytes of buf have been written. */ + while (w < nbytes) { + /* Keep trying until write succeeds without interruption. */ + do { + r = write(fd, buf + w, nbytes - w); + } while (r < 0 && errno == EINTR); + + if (r < 0) { + return r; + } + + w += r; + } + + return w; +} + +static void +deliver(char *maildir, char *newfn) +{ + char fn[80 + LEN_ULONG * 2], host[64]; + char *buildfn; /* used to build up the fn */ + time_t now; + struct tm *t; + pid_t pid; + struct stat statbuf; + int try; + int fd, r; + char buf[BUFSIZ]; + + if (chdir(maildir) != 0) { + err(EX_TEMPFAIL, "Failed chdir(%s)", maildir); + } + + pid = getpid(); + gethostname(host, sizeof(host)); + + for (try = 0; ; try++) { + now = time(NULL); + t = gmtime(&now); + + buildfn = fn + sprintf(fn, "tmp/"); + buildfn += strftime(buildfn, LEN_ULONG + 1, "%s.", t); + buildfn += snprintf(buildfn, LEN_ULONG + 1, "%d.", pid); + buildfn += sprintf(buildfn, "%s", host); + + if (stat(fn, &statbuf) != 0) { + if (errno == ENOENT) { + break; + } + } + + if (try == 2) { + errx(EX_TEMPFAIL, "Attempted filename exists."); + } + + sleep(2); + } + + fd = open(fn, O_WRONLY | O_EXCL | O_CREAT, 0644); + if (fd < 0) { + err(EX_TEMPFAIL, "Failed open(%s)", fn); + } + + /* From this point, we have a tmp file, so if someting fails we + * must try to unlink it before bombing. */ + + while ((r = read(STDIN_FILENO, buf, BUFSIZ)) > 0) { + if (full_write(fd, buf, r) != r) { + unlink(fn); + err(EX_TEMPFAIL, "Failed write(%s)", fn); + } + } + + if (fsync(fd) < 0) { + unlink(fn); + err(EX_TEMPFAIL, "Failed fsync(%s)", fn); + } + if (close(fd) < 0) { + unlink(fn); + err(EX_TEMPFAIL, "Failed close(%s)", fn); + } + + strcpy(newfn, fn); + memcpy(newfn, "new", 3); + + if (rename(fn, newfn) != 0) { + unlink(fn); + err(EX_TEMPFAIL, "Failed rename(%s, new)", fn); + } +} + +int +main(int argc, char *argv[]) +{ + char newfn[80 + LEN_ULONG * 2]; + pid_t pid; + FILE *errfile; + char *fail; + int fd; + + deliver("Maildir", newfn); + + if (argv[1]) { + if ((pid = fork()) > 0) { + return 0; + } else if (pid == 0) { + argv[0] = argv[1]; + argv[1] = newfn; + execvp(argv[0], argv); + fail = "exec"; + } else { + fail = "fork"; + } + + /* If we got here fork or exec failed, so try to write an + * error message where later mail processors can find it. We + * really can't do anything about it if this fails. */ + memcpy(newfn, "err", 3); + fd = open(newfn, O_WRONLY | O_EXCL | O_CREAT, 0644); + errfile = fdopen(fd, "w"); + if (errfile) { + fprintf(errfile, "%s:%d\n", fail, errno); + } + } + + /* Always return 0 because deliver exits if it fails, and if it + * succeeds we cannot return a failure code to the MTA. */ + return 0; +}