* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char copyright
[] =
"@(#) Copyright (c) 1990, 1993\n\
The Regents of the University of California. All rights reserved.\n";
static char sccsid
[] = "@(#)mail.local.c 8.1 (Berkeley) 6/4/93";
int eval
= EX_OK
; /* sysexits.h error value. */
void deliver
__P((int, char *));
void e_to_sys
__P((int));
__dead
void err
__P((const char *, ...));
void notifybiff
__P((char *));
void vwarn
__P((const char *, _BSD_VA_LIST_
));
void warn
__P((const char *, ...));
openlog("mail.local", 0, LOG_MAIL
);
while ((ch
= getopt(argc
, argv
, "df:r:")) != EOF
)
case 'd': /* Backward compatible. */
case 'r': /* Backward compatible. */
warn("multiple -f options");
* If from not specified, use the name from getlogin() if the
* uid matches, otherwise, use the name from the password file
* corresponding to the uid.
if (!from
&& (!(from
= getlogin()) ||
!(pw
= getpwnam(from
)) || pw
->pw_uid
!= uid
))
from
= (pw
= getpwuid(uid
)) ? pw
->pw_name
: "???";
* There is no way to distinguish the error status of one delivery
* from the rest of the deliveries. So, if we failed hard on one
* or more deliveries, but had no failures on any of the others, we
* return a hard failure. If we failed temporarily on one or more
* deliveries, we return a temporary failure regardless of the other
* failures. This results in the delivery being reattempted later
* at the expense of repeated failures and multiple deliveries.
for (fd
= store(from
); *argv
; ++argv
)
tn
= strdup(_PATH_LOCTMP
);
if ((fd
= mkstemp(tn
)) == -1 || (fp
= fdopen(fd
, "w+")) == NULL
) {
err("unable to open temporary file");
(void)fprintf(fp
, "From %s %s", from
, ctime(&tval
));
for (eline
= 1; fgets(line
, sizeof(line
), stdin
);) {
if (eline
&& line
[0] == 'F' &&
!memcmp(line
, "From ", 5))
(void)fprintf(fp
, "%s", line
);
err("temporary file write error");
/* If message not newline terminated, need an extra. */
/* Output a newline; note, empty messages are allowed. */
if (fflush(fp
) == EOF
|| ferror(fp
)) {
err("temporary file write error");
char biffmsg
[100], buf
[8*1024], path
[MAXPATHLEN
];
* Disallow delivery to unknown names -- special mailboxes can be
* handled in the sendmail aliases file.
if (!(pw
= getpwnam(name
))) {
warn("unknown name: %s", name
);
(void)snprintf(path
, sizeof(path
), "%s/%s", _PATH_MAILDIR
, name
);
* If the mailbox is a linked or a symlink, fail.
* If we created the mailbox, set the owner/group. If that fails,
* just return. Another process may have already opened it, so we
* can't unlink it. Historically, binmail set the owner/group at
* each mail delivery. We no longer do this, assuming that if the
* ownership or permissions were changed there was a reason.
* open(2) should support flock'ing the file.
O_APPEND
|O_CREAT
|O_EXCL
|O_WRONLY
, S_IRUSR
|S_IWUSR
)) < 0)
mbfd
= open(path
, O_APPEND
|O_WRONLY
, 0);
else if (fchown(mbfd
, pw
->pw_uid
, pw
->pw_gid
)) {
warn("chown %u.%u: %s", pw
->pw_uid
, pw
->pw_gid
, name
);
} else if (sb
.st_nlink
!= 1 || S_ISLNK(sb
.st_mode
)) {
warn("%s: linked file", path
);
mbfd
= open(path
, O_APPEND
|O_WRONLY
, 0);
warn("%s: %s", path
, strerror(errno
));
/* Wait until we can get a lock on the file. */
if (flock(mbfd
, LOCK_EX
)) {
warn("%s: %s", path
, strerror(errno
));
/* Get the starting offset of the new message for biff. */
curoff
= lseek(mbfd
, (off_t
)0, SEEK_END
);
(void)snprintf(biffmsg
, sizeof(biffmsg
), "%s@%qd\n", name
, curoff
);
/* Copy the message into the file. */
if (lseek(fd
, (off_t
)0, SEEK_SET
) == (off_t
)-1) {
warn("temporary file: %s", strerror(errno
));
while ((nr
= read(fd
, buf
, sizeof(buf
))) > 0)
for (off
= 0; off
< nr
; nr
-= nw
, off
+= nw
)
if ((nw
= write(mbfd
, buf
+ off
, nr
)) < 0) {
warn("%s: %s", path
, strerror(errno
));
warn("temporary file: %s", strerror(errno
));
/* Flush to disk, don't wait for update. */
warn("%s: %s", path
, strerror(errno
));
err2
: (void)ftruncate(mbfd
, curoff
);
/* Close and check -- NFS doesn't write until the close. */
warn("%s: %s", path
, strerror(errno
));
static struct sockaddr_in addr
;
/* Be silent if biff service not available. */
if (!(sp
= getservbyname("biff", "udp")))
if (!(hp
= gethostbyname("localhost"))) {
warn("localhost: %s", strerror(errno
));
addr
.sin_family
= hp
->h_addrtype
;
memmove(&addr
.sin_addr
, hp
->h_addr
, hp
->h_length
);
addr
.sin_port
= sp
->s_port
;
if (f
< 0 && (f
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
warn("socket: %s", strerror(errno
));
if (sendto(f
, msg
, len
, 0, (struct sockaddr
*)&addr
, sizeof(addr
))
warn("sendto biff: %s", strerror(errno
));
err("usage: mail.local [-f from] user ...");
err(const char *fmt
, ...)
warn(const char *fmt
, ...)
* Log the message to stderr.
* Don't use LOG_PERROR as an openlog() flag to do this,
* it's not portable enough.
(void)fprintf(stderr
, "mail.local: ");
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");
/* Log the message to syslog. */
vsyslog(LOG_ERR
, fmt
, ap
);
* Guess which errno's are temporary. Gag me.
/* Temporary failures override hard errors. */
switch(num
) { /* Hopefully temporary errors. */
case EAGAIN
: /* Resource temporarily unavailable */
case EDQUOT
: /* Disc quota exceeded */
case EBUSY
: /* Device busy */
case EPROCLIM
: /* Too many processes */
case EUSERS
: /* Too many users */
case ECONNABORTED
: /* Software caused connection abort */
case ECONNREFUSED
: /* Connection refused */
case ECONNRESET
: /* Connection reset by peer */
case EDEADLK
: /* Resource deadlock avoided */
case EFBIG
: /* File too large */
case EHOSTDOWN
: /* Host is down */
case EHOSTUNREACH
: /* No route to host */
case EMFILE
: /* Too many open files */
case ENETDOWN
: /* Network is down */
case ENETRESET
: /* Network dropped connection on reset */
case ENETUNREACH
: /* Network is unreachable */
case ENFILE
: /* Too many open files in system */
case ENOBUFS
: /* No buffer space available */
case ENOMEM
: /* Cannot allocate memory */
case ENOSPC
: /* No space left on device */
case EROFS
: /* Read-only file system */
case ESTALE
: /* Stale NFS file handle */
case ETIMEDOUT
: /* Connection timed out */
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
case EWOULDBLOCK
: /* Operation would block. */