]>
diplodocus.org Git - mdeliver/blob - mdeliver.c
70f24ea4a18dd5743db7d3b719c932870b253e74
1 /* mdeliver.c -- Deliver a message to a Maildir.
5 * This program is in the public domain.
7 * mdeliver is a simple, robust MDA. It delivers a single message
8 * from stdin to the user's Maildir and executes (in the background) a
9 * single program with the message filename as the first argument.
10 * Additional arguments may be specified; see main() below for
13 * This program does not conform strictly to POSIX. However, the two
14 * nonstandard interfaces it relies upon (<err.h> and <sysexits.h>)
15 * are widespread and easily enough replaced if necessary.
18 #include <sys/types.h>
32 #if !defined(HOST_NAME_MAX)
33 #if defined MAXHOSTNAMELEN
34 #define HOST_NAME_MAX MAXHOSTNAMELEN
36 #define HOST_NAME_MAX 64
40 /* holds 2^128 - 1 in decimal, plus '\0' */
43 /* 1 for '\0', 4 for "tmp/", HOST_NAME_MAX for the hostname, and two
44 * LEN_ULONGs for time and pid. */
45 #define TMPNAMLEN 1 + 4 + HOST_NAME_MAX + LEN_ULONG + LEN_ULONG
47 static char rcsid
[] = "$Id$";
49 /* Write all of buf, even if write(2) is interrupted. */
51 full_write(int fd
, const char *buf
, size_t nbytes
)
55 /* Loop until nbytes of buf have been written. */
57 /* Keep trying until write succeeds without interruption. */
59 r
= write(fd
, buf
+ w
, nbytes
- w
);
60 } while (r
< 0 && errno
== EINTR
);
73 get_filename(char *fn
)
76 char host
[HOST_NAME_MAX
+ 1];
80 char *buildfn
; /* used to build up the fn */
85 if (gethostname(host
, sizeof(host
)) != 0) {
86 err(EX_TEMPFAIL
, "Failed gethostname");
89 for (try = 0; ; try++) {
93 buildfn
= fn
+ sprintf(fn
, "tmp/");
94 buildfn
+= strftime(buildfn
, LEN_ULONG
+ 1, "%s.", t
);
95 buildfn
+= snprintf(buildfn
, LEN_ULONG
+ 1, "%d.", pid
);
96 buildfn
+= sprintf(buildfn
, "%s", host
);
98 if (stat(fn
, &statbuf
) != 0) {
99 if (errno
== ENOENT
) {
105 errx(EX_TEMPFAIL
, "Attempted filename exists.");
112 /* buf is passed only as an (early, evil) space optimization). */
114 strip_from(int fd
, char *buf
, char *fn
)
118 r
= read(STDIN_FILENO
, buf
, 5);
121 err(EX_TEMPFAIL
, "Failed read from stdin");
124 if (strncmp(buf
, "From ", 5)) {
125 while ((r
= read(STDIN_FILENO
, buf
, 1)) > 0
126 || (r
== -1 && errno
== EINTR
)) {
127 if (buf
[0] == '\n') {
134 err(EX_TEMPFAIL
, "Failed read from stdin");
137 if (full_write(fd
, buf
, r
) != r
) {
139 err(EX_TEMPFAIL
, "Failed write(%s)", fn
);
144 /* buf is passed only as an (early, evil) space optimization). */
146 copy_message(int fd
, char *buf
, char *fn
)
150 while ((r
= read(STDIN_FILENO
, buf
, BUFSIZ
)) > 0
151 || (r
== -1 && errno
== EINTR
)) {
152 if (full_write(fd
, buf
, r
) != r
) {
154 err(EX_TEMPFAIL
, "Failed write(%s)", fn
);
160 err(EX_TEMPFAIL
, "Failed read from stdin");
165 err(EX_TEMPFAIL
, "Failed fsync(%s)", fn
);
170 err(EX_TEMPFAIL
, "Failed close(%s)", fn
);
175 deliver(char *maildir
, char *newfn
)
179 char buf
[BUFSIZ
]; /* Allocated here only as an (early,
180 * evil) space optimization. */
182 if (chdir(maildir
) != 0) {
183 err(EX_TEMPFAIL
, "Failed chdir(%s)", maildir
);
188 fd
= open(fn
, O_WRONLY
| O_EXCL
| O_CREAT
, 0600);
190 err(EX_TEMPFAIL
, "Failed open(%s)", fn
);
193 /* From this point, we have a tmp file, so if someting fails we
194 * must try to unlink it before bombing. */
196 strip_from(fd
, buf
, fn
);
197 copy_message(fd
, buf
, fn
);
200 memcpy(newfn
, "new", 3);
202 if (rename(fn
, newfn
) != 0) {
204 err(EX_TEMPFAIL
, "Failed rename(%s, new)", fn
);
209 main(int argc
, char *argv
[])
212 char newfn
[TMPNAMLEN
];
218 if (argv
[1] && argv
[1][0] == '-' && argv
[1][1] == 'v') {
223 if (!(home
= getenv("HOME"))) {
224 err(EX_TEMPFAIL
, "Failed getenv(HOME)");
227 if (chdir(home
) != 0) {
228 err(EX_TEMPFAIL
, "Failed chdir(HOME)");
231 deliver("Maildir", newfn
);
234 if ((pid
= fork()) > 0) {
236 } else if (pid
== 0) {
239 execv(argv
[0], argv
);
245 /* If we got here fork or exec failed, so try to write an
246 * error message where later mail processors can find it. We
247 * really can't do anything about it if this fails. */
248 memcpy(newfn
, "err", 3);
250 fd
= open(newfn
, O_WRONLY
| O_EXCL
| O_CREAT
, 0644);
251 errfile
= fdopen(fd
, "w");
253 fprintf(errfile
, "%s:%d\n", fail
, errno
);
257 /* Always return 0 because deliver exits if it fails, and if it
258 * succeeds we cannot return a failure code to the MTA. */