145976Sbostic /*-
266733Sbostic  * Copyright (c) 1990, 1993, 1994
361434Sbostic  *	The Regents of the University of California.  All rights reserved.
445976Sbostic  *
545976Sbostic  * %sccs.include.redist.c%
645976Sbostic  */
745976Sbostic 
814465Ssam #ifndef lint
961434Sbostic static char copyright[] =
1066733Sbostic "@(#) Copyright (c) 1990, 1993, 1994\n\
1161434Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1245976Sbostic #endif /* not lint */
1314465Ssam 
1445976Sbostic #ifndef lint
15*69603Seric static char sccsid[] = "@(#)mail.local.c	8.19 (Berkeley) 05/22/95";
1645976Sbostic #endif /* not lint */
1745976Sbostic 
1868202Seric /*
1968202Seric  * This is not intended to compile on System V derived systems
2068202Seric  * such as Solaris or HP-UX, since they use a totally different
2168202Seric  * approach to mailboxes (essentially, they have a setgid program
2268202Seric  * rather than setuid, and they rely on the ability to "give away"
2368202Seric  * files to do their work).  IT IS NOT A BUG that this doesn't
2468202Seric  * compile on such architectures.
2568202Seric  */
2668202Seric 
2729763Skarels #include <sys/param.h>
2816731Sralph #include <sys/stat.h>
2945976Sbostic #include <sys/socket.h>
3057648Sbostic 
3145976Sbostic #include <netinet/in.h>
3257648Sbostic 
3357648Sbostic #include <errno.h>
3446672Sbostic #include <fcntl.h>
3545976Sbostic #include <netdb.h>
3645976Sbostic #include <pwd.h>
37579Sroot #include <stdio.h>
3846672Sbostic #include <stdlib.h>
3945976Sbostic #include <string.h>
4059601Sbostic #include <sysexits.h>
4157648Sbostic #include <syslog.h>
4257648Sbostic #include <time.h>
4357648Sbostic #include <unistd.h>
4468201Seric #include <ctype.h>
4557648Sbostic 
4659601Sbostic #if __STDC__
4759601Sbostic #include <stdarg.h>
4859601Sbostic #else
4959601Sbostic #include <varargs.h>
5059601Sbostic #endif
5159601Sbostic 
5267830Seric #ifndef LOCK_EX
5367830Seric # include <sys/file.h>
5467830Seric #endif
55579Sroot 
5667830Seric #ifdef BSD4_4
5767830Seric # include "pathnames.h"
5867830Seric #endif
5967830Seric 
6067830Seric #ifndef __P
6167830Seric # ifdef __STDC__
6267830Seric #  define __P(protos)	protos
6367830Seric # else
6467830Seric #  define __P(protos)	()
6567830Seric #  define const
6667830Seric # endif
6767830Seric #endif
6867830Seric #ifndef __dead
6967830Seric # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
7067830Seric #  define __dead	__volatile
7167830Seric # else
7267830Seric #  define __dead
7367830Seric # endif
7467830Seric #endif
7567830Seric 
7667830Seric #ifndef BSD4_4
7767830Seric # define _BSD_VA_LIST_	va_list
7867830Seric extern char	*strerror __P((int));
79*69603Seric extern int	snprintf __P((char *, int, const char *, ...));
8067830Seric #endif
8167830Seric 
82*69603Seric /*
83*69603Seric  * Compile with -DHAS_SAVED_IDS=0 if you don't have saved uids.  It will
84*69603Seric  * swap the effective uid (root) into the real uid using setreuid(),
85*69603Seric  * setting the effective uid to the recipient user, and then swap root
86*69603Seric  * back to effective uid when done.
87*69603Seric  *
88*69603Seric  * Use -DHAS_SAVED_IDS=1 if you can use seteuid(x) several times for
89*69603Seric  * various values of "x" -- that is, if your system will notice that you
90*69603Seric  * were originally invoked as root, and hence will allow future swaps.
91*69603Seric  *
92*69603Seric  * The following heuristic works for most common systems.  Note that
93*69603Seric  * SunOS claims to have _POSIX_SAVED_IDS, but doesn't -- but it does
94*69603Seric  * have an older version of _POSIX_VERSION.
95*69603Seric  *
96*69603Seric  * If you have a pure Posix system that does not have seteuid() or
97*69603Seric  * setreuid() (emulations don't count!) then you are out of luck.
98*69603Seric  */
99*69603Seric 
100*69603Seric #ifndef HASSAVEDUIDS
101*69603Seric # if defined(_POSIX_SAVED_IDS) && _POSIX_VERSION >= 199009L
102*69603Seric #  define HAS_SAVED_IDS	1
103*69603Seric # else
104*69603Seric #  define HAS_SAVED_IDS	0
105*69603Seric # endif
106*69603Seric #endif
107*69603Seric 
108*69603Seric #ifdef __hpux
109*69603Seric # define seteuid(e)	setresuid(-1, e, -1)
110*69603Seric #endif
111*69603Seric 
11267830Seric #ifndef _PATH_LOCTMP
11367830Seric # define _PATH_LOCTMP	"/tmp/local.XXXXXX"
11467830Seric #endif
11567830Seric #ifndef _PATH_MAILDIR
11667830Seric # define _PATH_MAILDIR	"/var/spool/mail"
11767830Seric #endif
11867830Seric 
11968188Seric #ifndef S_ISREG
12068188Seric # define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
12167830Seric #endif
12267830Seric 
12359601Sbostic int eval = EX_OK;			/* sysexits.h error value. */
124579Sroot 
12559601Sbostic void		deliver __P((int, char *));
12659601Sbostic void		e_to_sys __P((int));
12759601Sbostic __dead void	err __P((const char *, ...));
12859601Sbostic void		notifybiff __P((char *));
12959601Sbostic int		store __P((char *));
13059601Sbostic void		usage __P((void));
13159601Sbostic void		vwarn __P((const char *, _BSD_VA_LIST_));
13259601Sbostic void		warn __P((const char *, ...));
13350092Sbostic 
13457648Sbostic int
135579Sroot main(argc, argv)
13645976Sbostic 	int argc;
13759601Sbostic 	char *argv[];
138579Sroot {
13945976Sbostic 	struct passwd *pw;
14059601Sbostic 	int ch, fd;
14145976Sbostic 	uid_t uid;
14245976Sbostic 	char *from;
14367830Seric 	extern char *optarg;
14467830Seric 	extern int optind;
145579Sroot 
14668190Seric 	/* make sure we have some open file descriptors */
14768190Seric 	for (fd = 10; fd < 30; fd++)
14868190Seric 		(void) close(fd);
14968190Seric 
15068190Seric 	/* use a reasonable umask */
15168191Seric 	(void) umask(0077);
15268190Seric 
15367830Seric #ifdef LOG_MAIL
15458400Sbostic 	openlog("mail.local", 0, LOG_MAIL);
15567830Seric #else
15667830Seric 	openlog("mail.local", 0);
15767830Seric #endif
15816731Sralph 
15945976Sbostic 	from = NULL;
16045976Sbostic 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
16145976Sbostic 		switch(ch) {
16259601Sbostic 		case 'd':		/* Backward compatible. */
16316731Sralph 			break;
16416731Sralph 		case 'f':
16559601Sbostic 		case 'r':		/* Backward compatible. */
16659601Sbostic 			if (from != NULL) {
16759601Sbostic 				warn("multiple -f options");
16859601Sbostic 				usage();
16959601Sbostic 			}
17045976Sbostic 			from = optarg;
171579Sroot 			break;
17245976Sbostic 		case '?':
17316731Sralph 		default:
17445976Sbostic 			usage();
17516731Sralph 		}
17645976Sbostic 	argc -= optind;
17745976Sbostic 	argv += optind;
178579Sroot 
17945976Sbostic 	if (!*argv)
18045976Sbostic 		usage();
181579Sroot 
18245976Sbostic 	/*
18345976Sbostic 	 * If from not specified, use the name from getlogin() if the
18445976Sbostic 	 * uid matches, otherwise, use the name from the password file
18545976Sbostic 	 * corresponding to the uid.
18645976Sbostic 	 */
18745976Sbostic 	uid = getuid();
18846034Sbostic 	if (!from && (!(from = getlogin()) ||
18946034Sbostic 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
19045976Sbostic 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
191579Sroot 
19259601Sbostic 	/*
19359601Sbostic 	 * There is no way to distinguish the error status of one delivery
19459601Sbostic 	 * from the rest of the deliveries.  So, if we failed hard on one
19559601Sbostic 	 * or more deliveries, but had no failures on any of the others, we
19659601Sbostic 	 * return a hard failure.  If we failed temporarily on one or more
19759601Sbostic 	 * deliveries, we return a temporary failure regardless of the other
19859601Sbostic 	 * failures.  This results in the delivery being reattempted later
19959601Sbostic 	 * at the expense of repeated failures and multiple deliveries.
20059601Sbostic 	 */
20159601Sbostic 	for (fd = store(from); *argv; ++argv)
20259601Sbostic 		deliver(fd, *argv);
20345976Sbostic 	exit(eval);
204579Sroot }
205579Sroot 
20657648Sbostic int
20745976Sbostic store(from)
20845976Sbostic 	char *from;
209579Sroot {
21045976Sbostic 	FILE *fp;
21145976Sbostic 	time_t tval;
21245976Sbostic 	int fd, eline;
21367830Seric 	char line[2048];
21467830Seric 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
215579Sroot 
21667830Seric 	strcpy(tmpbuf, _PATH_LOCTMP);
21767830Seric 	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
21859601Sbostic 		e_to_sys(errno);
21959601Sbostic 		err("unable to open temporary file");
22059601Sbostic 	}
22167830Seric 	(void)unlink(tmpbuf);
222579Sroot 
22345976Sbostic 	(void)time(&tval);
22445976Sbostic 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
225579Sroot 
22645976Sbostic 	line[0] = '\0';
22745976Sbostic 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
22845976Sbostic 		if (line[0] == '\n')
22945976Sbostic 			eline = 1;
23045976Sbostic 		else {
23160097Sbostic 			if (eline && line[0] == 'F' &&
23260097Sbostic 			    !memcmp(line, "From ", 5))
23345976Sbostic 				(void)putc('>', fp);
23445976Sbostic 			eline = 0;
23545976Sbostic 		}
23645976Sbostic 		(void)fprintf(fp, "%s", line);
23759601Sbostic 		if (ferror(fp)) {
23859601Sbostic 			e_to_sys(errno);
23959601Sbostic 			err("temporary file write error");
24059601Sbostic 		}
241579Sroot 	}
242579Sroot 
24345976Sbostic 	/* If message not newline terminated, need an extra. */
24459601Sbostic 	if (!strchr(line, '\n'))
24545976Sbostic 		(void)putc('\n', fp);
24645976Sbostic 	/* Output a newline; note, empty messages are allowed. */
24745976Sbostic 	(void)putc('\n', fp);
24811893Seric 
24959601Sbostic 	if (fflush(fp) == EOF || ferror(fp)) {
25059601Sbostic 		e_to_sys(errno);
25159601Sbostic 		err("temporary file write error");
25259601Sbostic 	}
25359601Sbostic 	return (fd);
254579Sroot }
255579Sroot 
25659601Sbostic void
25745976Sbostic deliver(fd, name)
25845976Sbostic 	int fd;
25945976Sbostic 	char *name;
260579Sroot {
26166735Sbostic 	struct stat fsb, sb;
26245976Sbostic 	struct passwd *pw;
26359601Sbostic 	int mbfd, nr, nw, off;
26468201Seric 	char *p;
26545976Sbostic 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
26654203Sbostic 	off_t curoff;
267579Sroot 
26845976Sbostic 	/*
26945976Sbostic 	 * Disallow delivery to unknown names -- special mailboxes can be
27045976Sbostic 	 * handled in the sendmail aliases file.
27145976Sbostic 	 */
27245976Sbostic 	if (!(pw = getpwnam(name))) {
27359601Sbostic 		if (eval != EX_TEMPFAIL)
27459601Sbostic 			eval = EX_UNAVAILABLE;
27559601Sbostic 		warn("unknown name: %s", name);
27659601Sbostic 		return;
27745976Sbostic 	}
278579Sroot 
27968201Seric 	/*
28068201Seric 	 * Keep name reasonably short to avoid buffer overruns.
28168201Seric 	 *	This isn't necessary on BSD because of the proper
28268201Seric 	 *	definition of snprintf(), but it can cause problems
28368201Seric 	 *	on other systems.
28468201Seric 	 * Also, clear out any bogus characters.
28568201Seric 	 */
28668201Seric 
28768201Seric 	if (strlen(name) > 40)
28868201Seric 		name[40] = '\0';
28968201Seric 	for (p = name; *p != '\0'; p++)
29068201Seric 	{
29168201Seric 		if (!isascii(*p))
29268201Seric 			*p &= 0x7f;
29368201Seric 		else if (!isprint(*p))
29468201Seric 			*p = '.';
29568201Seric 	}
29668201Seric 
29757648Sbostic 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
298579Sroot 
299579Sroot 	/*
30066733Sbostic 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
30166733Sbostic 	 * race here, that the file was replaced with a symbolic link after
30266735Sbostic 	 * the lstat returned, but before the open.  We attempt to detect
30366735Sbostic 	 * this by comparing the original stat information and information
30466735Sbostic 	 * returned by an fstat of the file descriptor returned by the open.
30559601Sbostic 	 *
30666735Sbostic 	 * NB: this is a symptom of a larger problem, that the mail spooling
30766735Sbostic 	 * directory is writeable by the wrong users.  If that directory is
30866735Sbostic 	 * writeable, system security is compromised for other reasons, and
30966735Sbostic 	 * it cannot be fixed here.
31066735Sbostic 	 *
31159601Sbostic 	 * If we created the mailbox, set the owner/group.  If that fails,
31259601Sbostic 	 * just return.  Another process may have already opened it, so we
31359601Sbostic 	 * can't unlink it.  Historically, binmail set the owner/group at
31459601Sbostic 	 * each mail delivery.  We no longer do this, assuming that if the
31559601Sbostic 	 * ownership or permissions were changed there was a reason.
31659601Sbostic 	 *
31759601Sbostic 	 * XXX
31859601Sbostic 	 * open(2) should support flock'ing the file.
319579Sroot 	 */
32066736Sbostic tryagain:
32168005Seric 	lockmbox(path);
32259601Sbostic 	if (lstat(path, &sb)) {
32366736Sbostic 		mbfd = open(path,
32466736Sbostic 		    O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
32566736Sbostic 		if (mbfd == -1) {
32666736Sbostic 			if (errno == EEXIST)
32766736Sbostic 				goto tryagain;
32866736Sbostic 		} else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
32959601Sbostic 			e_to_sys(errno);
33059601Sbostic 			warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
331*69603Seric 			goto err1;
33259601Sbostic 		}
33368188Seric 	} else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
33459601Sbostic 		e_to_sys(errno);
33568188Seric 		warn("%s: irregular file", path);
336*69603Seric 		goto err0;
33768060Seric 	} else if (sb.st_uid != pw->pw_uid) {
33868060Seric 		warn("%s: wrong ownership (%d)", path, sb.st_uid);
33968060Seric 		unlockmbox();
34068060Seric 		return;
34166733Sbostic 	} else {
34259601Sbostic 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
34366735Sbostic 		if (mbfd != -1 &&
34466735Sbostic 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
34568188Seric 		    !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
34668188Seric 		    sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
34766735Sbostic 			warn("%s: file changed after open", path);
348*69603Seric 			goto err1;
34966733Sbostic 		}
35066733Sbostic 	}
35159601Sbostic 
35259601Sbostic 	if (mbfd == -1) {
35359601Sbostic 		e_to_sys(errno);
35459601Sbostic 		warn("%s: %s", path, strerror(errno));
355*69603Seric 		goto err0;
35645976Sbostic 	}
357579Sroot 
35859601Sbostic 	/* Wait until we can get a lock on the file. */
35945976Sbostic 	if (flock(mbfd, LOCK_EX)) {
36059601Sbostic 		e_to_sys(errno);
36159601Sbostic 		warn("%s: %s", path, strerror(errno));
36259601Sbostic 		goto err1;
36345976Sbostic 	}
364579Sroot 
36559601Sbostic 	/* Get the starting offset of the new message for biff. */
36654203Sbostic 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
36768021Seric 	(void)snprintf(biffmsg, sizeof(biffmsg),
36868021Seric 		sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
36968021Seric 		name, curoff);
37059601Sbostic 
37159601Sbostic 	/* Copy the message into the file. */
37254203Sbostic 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
37359601Sbostic 		e_to_sys(errno);
37459601Sbostic 		warn("temporary file: %s", strerror(errno));
37559601Sbostic 		goto err1;
37645976Sbostic 	}
377*69603Seric #if HAS_SAVED_IDS
378*69603Seric 	if (seteuid(pw->pw_uid) < 0) {
379*69603Seric 		e_to_sys(errno);
380*69603Seric 		warn("seteuid(%d): %s", pw->pw_uid, strerror(errno));
381*69603Seric 		goto err1;
382*69603Seric 	}
383*69603Seric #else
384*69603Seric 	if (setreuid(0, pw->pw_uid) < 0) {
385*69603Seric 		e_to_sys(errno);
386*69603Seric 		warn("setreuid(0, %d): %s (r=%d, e=%d)",
387*69603Seric 		     pw->pw_uid, strerror(errno), getuid(), geteuid());
388*69603Seric 		goto err1;
389*69603Seric 	}
390*69603Seric #endif
391*69603Seric #ifdef DEBUG
392*69603Seric 	printf("new euid = %d\n", geteuid());
393*69603Seric #endif
39445976Sbostic 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
39568201Seric 		for (off = 0; off < nr; off += nw)
39668201Seric 			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
39759601Sbostic 				e_to_sys(errno);
39859601Sbostic 				warn("%s: %s", path, strerror(errno));
399*69603Seric 				goto err3;
40045976Sbostic 			}
40145976Sbostic 	if (nr < 0) {
40259601Sbostic 		e_to_sys(errno);
40359601Sbostic 		warn("temporary file: %s", strerror(errno));
404*69603Seric 		goto err3;
405579Sroot 	}
406579Sroot 
40759601Sbostic 	/* Flush to disk, don't wait for update. */
40859601Sbostic 	if (fsync(mbfd)) {
40959601Sbostic 		e_to_sys(errno);
41059601Sbostic 		warn("%s: %s", path, strerror(errno));
411*69603Seric err3:
412*69603Seric #if !HAS_SAVED_IDS
413*69603Seric 		if (setreuid(0, 0) < 0) {
414*69603Seric 			e_to_sys(errno);
415*69603Seric 			warn("setreuid(0, 0): %s", strerror(errno));
416*69603Seric 		}
417*69603Seric # ifdef DEBUG
418*69603Seric 		printf("reset euid = %d\n", geteuid());
419*69603Seric # endif
420*69603Seric #endif
42159601Sbostic err2:		(void)ftruncate(mbfd, curoff);
42259601Sbostic err1:		(void)close(mbfd);
423*69603Seric err0:		unlockmbox();
42459601Sbostic 		return;
42559601Sbostic 	}
42659601Sbostic 
42759601Sbostic 	/* Close and check -- NFS doesn't write until the close. */
42859601Sbostic 	if (close(mbfd)) {
42959601Sbostic 		e_to_sys(errno);
43059601Sbostic 		warn("%s: %s", path, strerror(errno));
43168005Seric 		unlockmbox();
43259601Sbostic 		return;
43359601Sbostic 	}
434579Sroot 
435*69603Seric #if !HAS_SAVED_IDS
436*69603Seric 	if (setreuid(0, 0) < 0) {
437*69603Seric 		e_to_sys(errno);
438*69603Seric 		warn("setreuid(0, 0): %s", strerror(errno));
439*69603Seric 	}
440*69603Seric # ifdef DEBUG
441*69603Seric 	printf("reset euid = %d\n", geteuid());
442*69603Seric # endif
443*69603Seric #endif
44468005Seric 	unlockmbox();
44559601Sbostic 	notifybiff(biffmsg);
446579Sroot }
447579Sroot 
44868005Seric /*
44968005Seric  * user.lock files are necessary for compatibility with other
45068005Seric  * systems, e.g., when the mail spool file is NFS exported.
45168005Seric  * Alas, mailbox locking is more than just a local matter.
45268005Seric  * EPA 11/94.
45368005Seric  */
45468005Seric 
45568201Seric char	lockname[MAXPATHLEN];
45668005Seric int	locked = 0;
45768005Seric 
45868005Seric lockmbox(path)
45968005Seric 	char *path;
46068005Seric {
46168005Seric 	int statfailed = 0;
46268005Seric 
46368005Seric 	if (locked)
46468005Seric 		return;
46568005Seric 	sprintf(lockname, "%s.lock", path);
46668005Seric 	for (;; sleep(5)) {
46768005Seric 		int fd;
46868005Seric 		struct stat st;
46968005Seric 		time_t now;
47068005Seric 
47168187Seric 		fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
47268187Seric 		if (fd >= 0) {
47368005Seric 			locked = 1;
47468187Seric 			close(fd);
47568005Seric 			return;
47668005Seric 		}
47768005Seric 		if (stat(lockname, &st) < 0) {
47868005Seric 			if (statfailed++ > 5)
47968005Seric 				return;
48068005Seric 			continue;
48168005Seric 		}
48268005Seric 		statfailed = 0;
48368005Seric 		time(&now);
48468005Seric 		if (now < st.st_ctime + 300)
48568005Seric 			continue;
48668005Seric 		unlink(lockname);
48768005Seric 	}
48868005Seric }
48968005Seric 
49068005Seric unlockmbox()
49168005Seric {
49268005Seric 	if (!locked)
49368005Seric 		return;
49468005Seric 	unlink(lockname);
49568005Seric 	locked = 0;
49668005Seric }
49768005Seric 
49850092Sbostic void
49916731Sralph notifybiff(msg)
50016731Sralph 	char *msg;
50116731Sralph {
50216731Sralph 	static struct sockaddr_in addr;
50316731Sralph 	static int f = -1;
50445976Sbostic 	struct hostent *hp;
50545976Sbostic 	struct servent *sp;
50645976Sbostic 	int len;
50716731Sralph 
50845976Sbostic 	if (!addr.sin_family) {
50945976Sbostic 		/* Be silent if biff service not available. */
51045976Sbostic 		if (!(sp = getservbyname("biff", "udp")))
51145976Sbostic 			return;
51245976Sbostic 		if (!(hp = gethostbyname("localhost"))) {
51359601Sbostic 			warn("localhost: %s", strerror(errno));
51445976Sbostic 			return;
51516731Sralph 		}
51645976Sbostic 		addr.sin_family = hp->h_addrtype;
51767830Seric 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
51845976Sbostic 		addr.sin_port = sp->s_port;
51916731Sralph 	}
52045976Sbostic 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
52159601Sbostic 		warn("socket: %s", strerror(errno));
52245976Sbostic 		return;
52316731Sralph 	}
52445976Sbostic 	len = strlen(msg) + 1;
52546672Sbostic 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
52646672Sbostic 	    != len)
52759601Sbostic 		warn("sendto biff: %s", strerror(errno));
52816731Sralph }
52916731Sralph 
53050092Sbostic void
53145976Sbostic usage()
532579Sroot {
53359601Sbostic 	eval = EX_USAGE;
53459601Sbostic 	err("usage: mail.local [-f from] user ...");
535579Sroot }
536579Sroot 
53750092Sbostic #if __STDC__
53865558Sbostic void
53959601Sbostic err(const char *fmt, ...)
54050092Sbostic #else
54164993Smckusick void
54259601Sbostic err(fmt, va_alist)
54359601Sbostic 	const char *fmt;
54459601Sbostic 	va_dcl
54550092Sbostic #endif
54659601Sbostic {
54759601Sbostic 	va_list ap;
54850092Sbostic 
54959601Sbostic #if __STDC__
55059601Sbostic 	va_start(ap, fmt);
55159601Sbostic #else
55259601Sbostic 	va_start(ap);
55359601Sbostic #endif
55459601Sbostic 	vwarn(fmt, ap);
55559601Sbostic 	va_end(ap);
55659601Sbostic 
55759601Sbostic 	exit(eval);
55859601Sbostic }
55959601Sbostic 
56050092Sbostic void
56150092Sbostic #if __STDC__
56259601Sbostic warn(const char *fmt, ...)
56350092Sbostic #else
56459601Sbostic warn(fmt, va_alist)
56559601Sbostic 	const char *fmt;
56650092Sbostic 	va_dcl
56750092Sbostic #endif
568579Sroot {
56945976Sbostic 	va_list ap;
57059601Sbostic 
57150092Sbostic #if __STDC__
57246554Sbostic 	va_start(ap, fmt);
57350092Sbostic #else
57450092Sbostic 	va_start(ap);
57550092Sbostic #endif
57659601Sbostic 	vwarn(fmt, ap);
57759601Sbostic 	va_end(ap);
57859601Sbostic }
57959601Sbostic 
58059601Sbostic void
58159601Sbostic vwarn(fmt, ap)
58259601Sbostic 	const char *fmt;
58359601Sbostic 	_BSD_VA_LIST_ ap;
58459601Sbostic {
58558400Sbostic 	/*
58659601Sbostic 	 * Log the message to stderr.
58759601Sbostic 	 *
58859601Sbostic 	 * Don't use LOG_PERROR as an openlog() flag to do this,
58959601Sbostic 	 * it's not portable enough.
59058400Sbostic 	 */
59159601Sbostic 	if (eval != EX_USAGE)
59259601Sbostic 		(void)fprintf(stderr, "mail.local: ");
59358396Sbostic 	(void)vfprintf(stderr, fmt, ap);
59458400Sbostic 	(void)fprintf(stderr, "\n");
59559601Sbostic 
59667830Seric #ifndef ultrix
59759601Sbostic 	/* Log the message to syslog. */
59845976Sbostic 	vsyslog(LOG_ERR, fmt, ap);
59967830Seric #else
60067830Seric 	{
60167830Seric 		char fmtbuf[10240];
60267830Seric 
60367830Seric 		(void) sprintf(fmtbuf, fmt, ap);
60467830Seric 		syslog(LOG_ERR, "%s", fmtbuf);
60567830Seric 	}
60667830Seric #endif
607579Sroot }
60859601Sbostic 
60959601Sbostic /*
61059601Sbostic  * e_to_sys --
61159601Sbostic  *	Guess which errno's are temporary.  Gag me.
61259601Sbostic  */
61359601Sbostic void
61459601Sbostic e_to_sys(num)
61559601Sbostic 	int num;
61659601Sbostic {
61759601Sbostic 	/* Temporary failures override hard errors. */
61859601Sbostic 	if (eval == EX_TEMPFAIL)
61959601Sbostic 		return;
62059601Sbostic 
62159601Sbostic 	switch(num) {		/* Hopefully temporary errors. */
62259601Sbostic #ifdef EAGAIN
62359601Sbostic 	case EAGAIN:		/* Resource temporarily unavailable */
62459601Sbostic #endif
62559601Sbostic #ifdef EDQUOT
62659601Sbostic 	case EDQUOT:		/* Disc quota exceeded */
62759601Sbostic #endif
62859601Sbostic #ifdef EBUSY
62959601Sbostic 	case EBUSY:		/* Device busy */
63059601Sbostic #endif
63159601Sbostic #ifdef EPROCLIM
63259601Sbostic 	case EPROCLIM:		/* Too many processes */
63359601Sbostic #endif
63459601Sbostic #ifdef EUSERS
63559601Sbostic 	case EUSERS:		/* Too many users */
63659601Sbostic #endif
63759601Sbostic #ifdef ECONNABORTED
63859601Sbostic 	case ECONNABORTED:	/* Software caused connection abort */
63959601Sbostic #endif
64059601Sbostic #ifdef ECONNREFUSED
64159601Sbostic 	case ECONNREFUSED:	/* Connection refused */
64259601Sbostic #endif
64359601Sbostic #ifdef ECONNRESET
64459601Sbostic 	case ECONNRESET:	/* Connection reset by peer */
64559601Sbostic #endif
64659601Sbostic #ifdef EDEADLK
64759601Sbostic 	case EDEADLK:		/* Resource deadlock avoided */
64859601Sbostic #endif
64959601Sbostic #ifdef EFBIG
65059601Sbostic 	case EFBIG:		/* File too large */
65159601Sbostic #endif
65259601Sbostic #ifdef EHOSTDOWN
65359601Sbostic 	case EHOSTDOWN:		/* Host is down */
65459601Sbostic #endif
65559601Sbostic #ifdef EHOSTUNREACH
65659601Sbostic 	case EHOSTUNREACH:	/* No route to host */
65759601Sbostic #endif
65859601Sbostic #ifdef EMFILE
65959601Sbostic 	case EMFILE:		/* Too many open files */
66059601Sbostic #endif
66159601Sbostic #ifdef ENETDOWN
66259601Sbostic 	case ENETDOWN:		/* Network is down */
66359601Sbostic #endif
66459601Sbostic #ifdef ENETRESET
66559601Sbostic 	case ENETRESET:		/* Network dropped connection on reset */
66659601Sbostic #endif
66759601Sbostic #ifdef ENETUNREACH
66859601Sbostic 	case ENETUNREACH:	/* Network is unreachable */
66959601Sbostic #endif
67059601Sbostic #ifdef ENFILE
67159601Sbostic 	case ENFILE:		/* Too many open files in system */
67259601Sbostic #endif
67359601Sbostic #ifdef ENOBUFS
67459601Sbostic 	case ENOBUFS:		/* No buffer space available */
67559601Sbostic #endif
67659601Sbostic #ifdef ENOMEM
67759601Sbostic 	case ENOMEM:		/* Cannot allocate memory */
67859601Sbostic #endif
67959601Sbostic #ifdef ENOSPC
68059601Sbostic 	case ENOSPC:		/* No space left on device */
68159601Sbostic #endif
68259601Sbostic #ifdef EROFS
68359601Sbostic 	case EROFS:		/* Read-only file system */
68459601Sbostic #endif
68559601Sbostic #ifdef ESTALE
68659601Sbostic 	case ESTALE:		/* Stale NFS file handle */
68759601Sbostic #endif
68859601Sbostic #ifdef ETIMEDOUT
68959601Sbostic 	case ETIMEDOUT:		/* Connection timed out */
69059601Sbostic #endif
69167830Seric #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
69259601Sbostic 	case EWOULDBLOCK:	/* Operation would block. */
69359601Sbostic #endif
69459601Sbostic 		eval = EX_TEMPFAIL;
69559601Sbostic 		break;
69659601Sbostic 	default:
69759601Sbostic 		eval = EX_UNAVAILABLE;
69859601Sbostic 		break;
69959601Sbostic 	}
70059601Sbostic }
70167830Seric 
70267830Seric #ifndef BSD4_4
70367830Seric 
70467830Seric char *
70567830Seric strerror(eno)
70667830Seric 	int eno;
70767830Seric {
70867830Seric 	extern int sys_nerr;
70967830Seric 	extern char *sys_errlist[];
71067830Seric 	static char ebuf[60];
71167830Seric 
71267830Seric 	if (eno >= 0 && eno <= sys_nerr)
71367830Seric 		return sys_errlist[eno];
71467830Seric 	(void) sprintf(ebuf, "Error %d", eno);
71567830Seric 	return ebuf;
71667830Seric }
71767830Seric 
71867830Seric #if __STDC__
71967830Seric snprintf(char *buf, int bufsiz, const char *fmt, ...)
72067830Seric #else
72167830Seric snprintf(buf, bufsiz, fmt, va_alist)
72267830Seric 	char *buf;
72367830Seric 	int bufsiz;
72467830Seric 	const char *fmt;
72567830Seric 	va_dcl
72667830Seric #endif
72767830Seric {
72867830Seric 	va_list ap;
72967830Seric 
73067830Seric #if __STDC__
73167830Seric 	va_start(ap, fmt);
73267830Seric #else
73367830Seric 	va_start(ap);
73467830Seric #endif
73567830Seric 	vsprintf(buf, fmt, ap);
73667830Seric 	va_end(ap);
73767830Seric }
73867830Seric 
73967830Seric #endif
74067830Seric 
74167830Seric #ifdef ultrix
74267830Seric 
74368189Seric /*
74468189Seric  * Copyright (c) 1987, 1993
74568189Seric  *	The Regents of the University of California.  All rights reserved.
74668189Seric  *
74768189Seric  * Redistribution and use in source and binary forms, with or without
74868189Seric  * modification, are permitted provided that the following conditions
74968189Seric  * are met:
75068189Seric  * 1. Redistributions of source code must retain the above copyright
75168189Seric  *    notice, this list of conditions and the following disclaimer.
75268189Seric  * 2. Redistributions in binary form must reproduce the above copyright
75368189Seric  *    notice, this list of conditions and the following disclaimer in the
75468189Seric  *    documentation and/or other materials provided with the distribution.
75568189Seric  * 3. All advertising materials mentioning features or use of this software
75668189Seric  *    must display the following acknowledgement:
75768189Seric  *	This product includes software developed by the University of
75868189Seric  *	California, Berkeley and its contributors.
75968189Seric  * 4. Neither the name of the University nor the names of its contributors
76068189Seric  *    may be used to endorse or promote products derived from this software
76168189Seric  *    without specific prior written permission.
76268189Seric  *
76368189Seric  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
76468189Seric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
76568189Seric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76668189Seric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
76768189Seric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
76868189Seric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
76968189Seric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77068189Seric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
77168189Seric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
77268189Seric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77368189Seric  * SUCH DAMAGE.
77468189Seric  */
77568189Seric 
77668189Seric #if defined(LIBC_SCCS) && !defined(lint)
77768189Seric static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
77868189Seric #endif /* LIBC_SCCS and not lint */
77968189Seric 
78068189Seric #include <sys/types.h>
78168189Seric #include <sys/stat.h>
78268189Seric #include <fcntl.h>
78368189Seric #include <errno.h>
78468189Seric #include <stdio.h>
78568189Seric #include <ctype.h>
78668189Seric 
78768189Seric static int _gettemp();
78868189Seric 
78968189Seric mkstemp(path)
79068189Seric 	char *path;
79167830Seric {
79267830Seric 	int fd;
79367830Seric 
79468189Seric 	return (_gettemp(path, &fd) ? fd : -1);
79567830Seric }
79667830Seric 
79768189Seric /*
79868189Seric char *
79968189Seric mktemp(path)
80068189Seric 	char *path;
80168189Seric {
80268189Seric 	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
80368189Seric }
80468189Seric */
80568189Seric 
80668189Seric static
80768189Seric _gettemp(path, doopen)
80868189Seric 	char *path;
80968189Seric 	register int *doopen;
81068189Seric {
81168189Seric 	extern int errno;
81268189Seric 	register char *start, *trv;
81368189Seric 	struct stat sbuf;
81468189Seric 	u_int pid;
81568189Seric 
81668189Seric 	pid = getpid();
81768189Seric 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
81868189Seric 	while (*--trv == 'X') {
81968189Seric 		*trv = (pid % 10) + '0';
82068189Seric 		pid /= 10;
82168189Seric 	}
82268189Seric 
82368189Seric 	/*
82468189Seric 	 * check the target directory; if you have six X's and it
82568189Seric 	 * doesn't exist this runs for a *very* long time.
82668189Seric 	 */
82768189Seric 	for (start = trv + 1;; --trv) {
82868189Seric 		if (trv <= path)
82968189Seric 			break;
83068189Seric 		if (*trv == '/') {
83168189Seric 			*trv = '\0';
83268189Seric 			if (stat(path, &sbuf))
83368189Seric 				return(0);
83468189Seric 			if (!S_ISDIR(sbuf.st_mode)) {
83568189Seric 				errno = ENOTDIR;
83668189Seric 				return(0);
83768189Seric 			}
83868189Seric 			*trv = '/';
83968189Seric 			break;
84068189Seric 		}
84168189Seric 	}
84268189Seric 
84368189Seric 	for (;;) {
84468189Seric 		if (doopen) {
84568189Seric 			if ((*doopen =
84668189Seric 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
84768189Seric 				return(1);
84868189Seric 			if (errno != EEXIST)
84968189Seric 				return(0);
85068189Seric 		}
85168189Seric 		else if (stat(path, &sbuf))
85268189Seric 			return(errno == ENOENT ? 1 : 0);
85368189Seric 
85468189Seric 		/* tricky little algorithm for backward compatibility */
85568189Seric 		for (trv = start;;) {
85668189Seric 			if (!*trv)
85768189Seric 				return(0);
85868189Seric 			if (*trv == 'z')
85968189Seric 				*trv++ = 'a';
86068189Seric 			else {
86168189Seric 				if (isdigit(*trv))
86268189Seric 					*trv = 'a';
86368189Seric 				else
86468189Seric 					++*trv;
86568189Seric 				break;
86668189Seric 			}
86768189Seric 		}
86868189Seric 	}
86968189Seric 	/*NOTREACHED*/
87068189Seric }
87168189Seric 
87267830Seric #endif
873