summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
f05e512)
on failure, not having to worry about dumbass /^From /, and handling
RPLINE and DTLINE.
Say EX_TEMPFAIL instead of MDELIVER_EXITCODE.
s/TMPNAMLEN/LEN_FILENAME/; it's not just used for the tmp file.
Random cleanup:
- drop some redundant comments
- s/statbuf/st/
- s/STDIN_FILENO/0/
- cleanup error messages
- other style nits
Drop <err.h> and implement custom err and errx, which doesn't need to
be passed an exit code.
Implement and use full_sleep to really sleep the specified time.
(get_filename): Don't break when gethostname truncates.
(strip_from): Don't even compile this for QMAIL.
(deliver): Take a new parameter, the subdirectory to deliver to,
instead of "new". #ifdef QMAIL, write RPLINE and DTLINE to tmp file
before copying stdin.
(deliver): Also had a huge problem: it was using the logic i came up
with for minc, to open an empty file for the target and then rename
onto that. That's fine for mh, which already loses mail this way
(once i switch refile to be like minc this will be fine), but since
other Maildir MDAs follow the official algorithm, this could clobber
mail. Switch to the official algorithm.
(run_processor): Use better error message format.
(main): If .mdeliver-processor exists, pass deliver "mdb" as the
subdirectory and then call run_processor, else pass "new".
* single program with the message filename as the first argument.
* Additional arguments may be specified; see main() below for
* details.
* single program with the message filename as the first argument.
* Additional arguments may be specified; see main() below for
* details.
- *
- * This program does not conform strictly to POSIX. However, the two
- * nonstandard interfaces it relies upon (<err.h> and <sysexits.h>)
- * are widespread and easily enough replaced if necessary.
*/
#include <sys/types.h>
#include <sys/stat.h>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
-#ifndef MDELIVER_EXITCODE
+#ifndef EX_TEMPFAIL
+#ifdef QMAIL
+#define EX_TEMPFAIL 111
+#else
-#define MDELIVER_EXITCODE EX_TEMPFAIL
#endif
#if !defined(HOST_NAME_MAX)
#endif
#if !defined(HOST_NAME_MAX)
/* holds 2^128 - 1 in decimal, plus '\0' */
#define LEN_ULONG 40
/* holds 2^128 - 1 in decimal, plus '\0' */
#define LEN_ULONG 40
-/* 1 for '\0', 4 for "tmp/", 2 for the two dots ('.'), HOST_NAME_MAX
- * for the hostname, and two LEN_ULONGs for time and pid. */
-#define TMPNAMLEN 1 + 4 + 2 + HOST_NAME_MAX + LEN_ULONG + LEN_ULONG
+/* 4 for "tmp/", 2 for the two dots ('.'), HOST_NAME_MAX for the
+ * hostname, two LEN_ULONGs for time and pid, and 1 for '\0', . */
+#define LEN_FILENAME 4 + 2 + HOST_NAME_MAX + LEN_ULONG + LEN_ULONG + 1
-/* Use this macro to perform some operation and saving the value errno
- * had before that operation.
- */
#define save_errno(op) { \
int olderrno = errno; \
op; \
#define save_errno(op) { \
int olderrno = errno; \
op; \
static char ident[] = "$Id$";
static char ident[] = "$Id$";
+#define err(...) _err(true, __LINE__, __VA_ARGS__)
+#define errx(...) _err(false, __LINE__, __VA_ARGS__)
+
+static void
+_err(bool want_strerror, int lineno, char *format, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "mdeliver:%d:", lineno);
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+
+ if (want_strerror) {
+ perror("");
+ } else {
+ fputs("\n", stderr);
+ }
+
+ exit(EX_TEMPFAIL);
+}
+
+static void
+full_sleep(unsigned seconds)
+{
+ while (seconds > 0) {
+ seconds = sleep(seconds);
+ }
+}
+
/* Write all of buf, even if write(2) is interrupted. */
static ssize_t
full_write(int fd, const char *buf, size_t nbytes)
/* Write all of buf, even if write(2) is interrupted. */
static ssize_t
full_write(int fd, const char *buf, size_t nbytes)
char host[HOST_NAME_MAX + 1];
int try;
time_t now;
char host[HOST_NAME_MAX + 1];
int try;
time_t now;
pid = getpid();
if (gethostname(host, sizeof(host)) != 0) {
pid = getpid();
if (gethostname(host, sizeof(host)) != 0) {
- err(MDELIVER_EXITCODE, "Failed gethostname");
+ /* http://www.opengroup.org/onlinepubs/009695399/functions/gethostname.html
+ * "it is unspecified whether the returned name is null-terminated" */
+ host[sizeof(host) - 1] = '\0';
for (try = 0; ; try++) {
now = time(NULL);
if (now == -1) {
for (try = 0; ; try++) {
now = time(NULL);
if (now == -1) {
- err(MDELIVER_EXITCODE, "Failed time()");
}
sprintf(fn, "tmp/%lu.%lu.%s",
(long unsigned)now, (long unsigned)pid, host);
}
sprintf(fn, "tmp/%lu.%lu.%s",
(long unsigned)now, (long unsigned)pid, host);
- if (stat(fn, &statbuf) != 0) {
+ if (stat(fn, &st) != 0) {
if (errno == ENOENT) {
/* Every failure point in this program relies on errno
* describing what went wrong. This is the one
if (errno == ENOENT) {
/* Every failure point in this program relies on errno
* describing what went wrong. This is the one
- errx(MDELIVER_EXITCODE, "Attempted filename exists.");
+ errx("Attempted filename exists.");
/* Strip the non-RFC2822-compliant 'From ' line some busted-ass
* mailers (such as Postfix) add as the first line of input. This bit
* of historical gunk originated with Berkeley mbox files, which use
/* Strip the non-RFC2822-compliant 'From ' line some busted-ass
* mailers (such as Postfix) add as the first line of input. This bit
* of historical gunk originated with Berkeley mbox files, which use
* bytes safely is not an unreasonable demand. If i can't get
* five measly bytes, something is probably wrong, so bomb.
*/
* bytes safely is not an unreasonable demand. If i can't get
* five measly bytes, something is probably wrong, so bomb.
*/
- r = read(STDIN_FILENO, buf, 5);
if (r != 5) {
save_errno( unlink(fn) );
if (r != 5) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed read from stdin");
}
if (strncmp(buf, "From ", 5) == 0) {
}
if (strncmp(buf, "From ", 5) == 0) {
- while ((r = read(STDIN_FILENO, buf, 1)) > 0
+ while ((r = read(0, buf, 1)) > 0
|| (r == -1 && errno == EINTR)) {
if (buf[0] == '\n') {
break;
|| (r == -1 && errno == EINTR)) {
if (buf[0] == '\n') {
break;
if (r == -1) {
save_errno( unlink(fn) );
if (r == -1) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed read from stdin");
}
} else {
if (full_write(fd, buf, r) != r) {
save_errno( unlink(fn) );
}
} else {
if (full_write(fd, buf, r) != r) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed write(%s)", fn);
/* Copy the file from INFD to OUTFD in blocks of BUFSIZE bytes (stored
* in BUF). BUF must point to a buffer of at least BUFSIZE bytes;
/* Copy the file from INFD to OUTFD in blocks of BUFSIZE bytes (stored
* in BUF). BUF must point to a buffer of at least BUFSIZE bytes;
|| (r == -1 && errno == EINTR)) {
if (full_write(outfd, buf, r) != r) {
save_errno( unlink(fn) );
|| (r == -1 && errno == EINTR)) {
if (full_write(outfd, buf, r) != r) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed write(%s)", fn);
}
}
if (r == -1) {
save_errno( unlink(fn) );
}
}
if (r == -1) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed read");
+ err("read(%d, %d)", infd, bufsize);
}
if (fsync(outfd) < 0) {
save_errno( unlink(fn) );
}
if (fsync(outfd) < 0) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed fsync(%s)", fn);
}
if (close(outfd) < 0) {
save_errno( unlink(fn) );
}
if (close(outfd) < 0) {
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed close(%s)", fn);
-deliver(char *maildir, char *newfn)
+deliver(char *maildir, char *sub, char *newfn)
- char fn[TMPNAMLEN];
- int fd, tmpfd;
+ char fn[LEN_FILENAME];
+ int fd;
char buf[BUFSIZ]; /* Allocated here only as an (early,
* evil) space optimization. */
if (chdir(maildir) != 0) {
char buf[BUFSIZ]; /* Allocated here only as an (early,
* evil) space optimization. */
if (chdir(maildir) != 0) {
- err(MDELIVER_EXITCODE, "Failed chdir(%s)", maildir);
+ err("chdir(%s)", maildir);
}
get_filename(fn);
fd = open(fn, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (fd < 0) {
}
get_filename(fn);
fd = open(fn, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (fd < 0) {
- err(MDELIVER_EXITCODE, "Failed open(%s)", fn);
}
/* From this point, we have a tmp file, so if someting fails we
* must try to unlink it before bombing. */
}
/* From this point, we have a tmp file, so if someting fails we
* must try to unlink it before bombing. */
+#ifdef QMAIL
+ {
+ char *buf;
+ size_t len;
+
+ buf = getenv("RPLINE");
+ len = strlen(buf);
+ if (full_write(fd, buf, len) != len) {
+ save_errno( unlink(fn) );
+ err("write(%s)", fn);
+ }
+ buf = getenv("DTLINE");
+ len = strlen(buf);
+ if (full_write(fd, buf, len) != len) {
+ save_errno( unlink(fn) );
+ err("write(%s)", fn);
+ }
+ }
+#else
- copy_file(STDIN_FILENO, fd, buf, BUFSIZ, fn);
- strcpy(newfn, fn);
- memcpy(newfn, "new", 3);
+ copy_file(0, fd, buf, BUFSIZ, fn);
- tmpfd = open(newfn, O_WRONLY | O_EXCL | O_CREAT, 0600);
- if (tmpfd < 0) {
- /* XXX: what error message to use? */
- err(MDELIVER_EXITCODE, "Failed open(%s)", newfn);
- }
- close(tmpfd);
+ strcpy(newfn, fn);
+ memcpy(newfn, sub, 3);
- if (rename(fn, newfn) != 0) {
- save_errno( unlink(newfn) );
+ if (link(fn, newfn) != 0) {
save_errno( unlink(fn) );
save_errno( unlink(fn) );
- err(MDELIVER_EXITCODE, "Failed rename(%s, new)", fn);
+ err("link(%s, %s)", fn, newfn);
+ save_errno( unlink(fn) );
}
/* Run a program to process the new message. */
}
/* Run a program to process the new message. */
if (fd >= 0) {
errfile = fdopen(fd, "w");
if (errfile) {
if (fd >= 0) {
errfile = fdopen(fd, "w");
if (errfile) {
- fprintf(errfile, "%s:%d\n", fail, olderrno);
+ fprintf(errfile, "mdeliver:mdeliver.c:%d:%s: %s\n",
+ __LINE__, fail, strerror(olderrno));
fflush(errfile);
}
fsync(fd);
fflush(errfile);
}
fsync(fd);
main(int argc, char *argv[])
{
char *home;
main(int argc, char *argv[])
{
char *home;
+ char newfn[LEN_FILENAME];
+ struct stat st;
if (argv[1] && argv[1][0] == '-' && argv[1][1] == 'v') {
puts(ident);
return 0;
}
if (argv[1] && argv[1][0] == '-' && argv[1][1] == 'v') {
puts(ident);
return 0;
}
- if (!(home = getenv("HOME"))) {
- err(MDELIVER_EXITCODE, "Failed getenv(HOME)");
+ if ((home = getenv("HOME")) == NULL) {
+ errx("HOME is not set.");
}
if (chdir(home) != 0) {
}
if (chdir(home) != 0) {
- err(MDELIVER_EXITCODE, "Failed chdir(HOME)");
+ err("chdir(%s)", home);
- deliver("Maildir", newfn);
-
- run_processor(home, newfn);
+ if (stat(".mdeliver-processor", &st) == 0) {
+ deliver("Maildir", "mdp", newfn);
+ run_processor(home, newfn);
+ } else {
+ deliver("Maildir", "new", newfn);
+ }
- /* Always return 0 because deliver exits if it fails, and if it
- * succeeds we cannot return a failure code to the MTA. */