* Copyright (c) 1990 The Regents of the University of California.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)mail.local.c 5.8 (Berkeley) %G%";
int deliver
__P((int, char *));
void err
__P((int, const char *, ...));
void notifybiff
__P((char *));
openlog("mail.local", LOG_PERROR
, LOG_MAIL
);
while ((ch
= getopt(argc
, argv
, "df:r:")) != EOF
)
case 'd': /* backward compatible */
case 'r': /* backward compatible */
err(FATAL
, "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
: "???";
for (eval
= 0; *argv
; ++argv
)
eval
|= deliver(fd
, *argv
);
tn
= strdup(_PATH_LOCTMP
);
if ((fd
= mkstemp(tn
)) == -1 || !(fp
= fdopen(fd
, "w+")))
err(FATAL
, "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' && !bcmp(line
, "From ", 5))
(void)fprintf(fp
, "%s", line
);
/* If message not newline terminated, need an extra. */
/* Output a newline; note, empty messages are allowed. */
err(FATAL
, "temporary file write error");
int created
, mbfd
, nr
, nw
, off
, rval
;
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
))) {
err(NOTFATAL
, "unknown name: %s", name
);
(void)sprintf(path
, "%s/%s", _PATH_MAILDIR
, name
);
if (!(created
= lstat(path
, &sb
)) &&
(sb
.st_nlink
!= 1 || S_ISLNK(sb
.st_mode
))) {
err(NOTFATAL
, "%s: linked file", path
);
* There's a race here -- two processes think they both created
* the file. This means the file cannot be unlinked.
open(path
, O_APPEND
|O_CREAT
|O_WRONLY
, S_IRUSR
|S_IWUSR
)) < 0) {
err(NOTFATAL
, "%s: %s", path
, strerror(errno
));
/* XXX: Open should allow flock'ing the file; see 4.4BSD. */
if (flock(mbfd
, LOCK_EX
)) {
err(NOTFATAL
, "%s: %s", path
, strerror(errno
));
curoff
= lseek(mbfd
, (off_t
)0, SEEK_END
);
(void)sprintf(biffmsg
, "%s@%qd\n", name
, curoff
);
if (lseek(fd
, (off_t
)0, SEEK_SET
) == (off_t
)-1) {
err(FATAL
, "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) {
err(NOTFATAL
, "%s: %s", path
, strerror(errno
));
err(FATAL
, "temporary file: %s", strerror(errno
));
trunc
: (void)ftruncate(mbfd
, curoff
);
* Set the owner and group. Historically, binmail repeated this at
* each mail delivery. We no longer do this, assuming that if the
* ownership or permissions were changed there was a reason for doing
(void)fchown(mbfd
, pw
->pw_uid
, pw
->pw_gid
);
(void)fsync(mbfd
); /* Don't wait for update. */
(void)close(mbfd
); /* Implicit unlock. */
static struct sockaddr_in addr
;
/* Be silent if biff service not available. */
if (!(sp
= getservbyname("biff", "udp")))
if (!(hp
= gethostbyname("localhost"))) {
err(NOTFATAL
, "localhost: %s", strerror(errno
));
addr
.sin_family
= hp
->h_addrtype
;
bcopy(hp
->h_addr
, &addr
.sin_addr
, hp
->h_length
);
addr
.sin_port
= sp
->s_port
;
if (f
< 0 && (f
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
err(NOTFATAL
, "socket: %s", strerror(errno
));
if (sendto(f
, msg
, len
, 0, (struct sockaddr
*)&addr
, sizeof(addr
))
err(NOTFATAL
, "sendto biff: %s", strerror(errno
));
err(FATAL
, "usage: mail.local [-f from] user ...");
err(int isfatal
, const char *fmt
, ...)
err(isfatal
, fmt
, va_alist
)
vsyslog(LOG_ERR
, fmt
, ap
);