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*67830Seric static char sccsid[] = "@(#)mail.local.c	8.7 (Berkeley) 10/17/94";
1645976Sbostic #endif /* not lint */
1745976Sbostic 
1829763Skarels #include <sys/param.h>
1916731Sralph #include <sys/stat.h>
2045976Sbostic #include <sys/socket.h>
2157648Sbostic 
2245976Sbostic #include <netinet/in.h>
2357648Sbostic 
2457648Sbostic #include <errno.h>
2546672Sbostic #include <fcntl.h>
2645976Sbostic #include <netdb.h>
2745976Sbostic #include <pwd.h>
28579Sroot #include <stdio.h>
2946672Sbostic #include <stdlib.h>
3045976Sbostic #include <string.h>
3159601Sbostic #include <sysexits.h>
3257648Sbostic #include <syslog.h>
3357648Sbostic #include <time.h>
3457648Sbostic #include <unistd.h>
3557648Sbostic 
3659601Sbostic #if __STDC__
3759601Sbostic #include <stdarg.h>
3859601Sbostic #else
3959601Sbostic #include <varargs.h>
4059601Sbostic #endif
4159601Sbostic 
42*67830Seric #ifndef LOCK_EX
43*67830Seric # include <sys/file.h>
44*67830Seric #endif
45579Sroot 
46*67830Seric #ifdef BSD4_4
47*67830Seric # include "pathnames.h"
48*67830Seric #endif
49*67830Seric 
50*67830Seric #ifndef __P
51*67830Seric # ifdef __STDC__
52*67830Seric #  define __P(protos)	protos
53*67830Seric # else
54*67830Seric #  define __P(protos)	()
55*67830Seric #  define const
56*67830Seric # endif
57*67830Seric #endif
58*67830Seric #ifndef __dead
59*67830Seric # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
60*67830Seric #  define __dead	__volatile
61*67830Seric # else
62*67830Seric #  define __dead
63*67830Seric # endif
64*67830Seric #endif
65*67830Seric 
66*67830Seric #ifndef BSD4_4
67*67830Seric # define _BSD_VA_LIST_	va_list
68*67830Seric extern char	*strerror __P((int));
69*67830Seric #endif
70*67830Seric 
71*67830Seric #ifndef _PATH_LOCTMP
72*67830Seric # define _PATH_LOCTMP	"/tmp/local.XXXXXX"
73*67830Seric #endif
74*67830Seric #ifndef _PATH_MAILDIR
75*67830Seric # define _PATH_MAILDIR	"/var/spool/mail"
76*67830Seric #endif
77*67830Seric 
78*67830Seric #ifndef S_ISLNK
79*67830Seric # define S_ISLNK(mode)	(((mode) & _S_IFMT) == S_IFLNK)
80*67830Seric #endif
81*67830Seric 
8259601Sbostic int eval = EX_OK;			/* sysexits.h error value. */
83579Sroot 
8459601Sbostic void		deliver __P((int, char *));
8559601Sbostic void		e_to_sys __P((int));
8659601Sbostic __dead void	err __P((const char *, ...));
8759601Sbostic void		notifybiff __P((char *));
8859601Sbostic int		store __P((char *));
8959601Sbostic void		usage __P((void));
9059601Sbostic void		vwarn __P((const char *, _BSD_VA_LIST_));
9159601Sbostic void		warn __P((const char *, ...));
9250092Sbostic 
9357648Sbostic int
94579Sroot main(argc, argv)
9545976Sbostic 	int argc;
9659601Sbostic 	char *argv[];
97579Sroot {
9845976Sbostic 	struct passwd *pw;
9959601Sbostic 	int ch, fd;
10045976Sbostic 	uid_t uid;
10145976Sbostic 	char *from;
102*67830Seric 	extern char *optarg;
103*67830Seric 	extern int optind;
104579Sroot 
105*67830Seric #ifdef LOG_MAIL
10658400Sbostic 	openlog("mail.local", 0, LOG_MAIL);
107*67830Seric #else
108*67830Seric 	openlog("mail.local", 0);
109*67830Seric #endif
11016731Sralph 
11145976Sbostic 	from = NULL;
11245976Sbostic 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
11345976Sbostic 		switch(ch) {
11459601Sbostic 		case 'd':		/* Backward compatible. */
11516731Sralph 			break;
11616731Sralph 		case 'f':
11759601Sbostic 		case 'r':		/* Backward compatible. */
11859601Sbostic 			if (from != NULL) {
11959601Sbostic 				warn("multiple -f options");
12059601Sbostic 				usage();
12159601Sbostic 			}
12245976Sbostic 			from = optarg;
123579Sroot 			break;
12445976Sbostic 		case '?':
12516731Sralph 		default:
12645976Sbostic 			usage();
12716731Sralph 		}
12845976Sbostic 	argc -= optind;
12945976Sbostic 	argv += optind;
130579Sroot 
13145976Sbostic 	if (!*argv)
13245976Sbostic 		usage();
133579Sroot 
13445976Sbostic 	/*
13545976Sbostic 	 * If from not specified, use the name from getlogin() if the
13645976Sbostic 	 * uid matches, otherwise, use the name from the password file
13745976Sbostic 	 * corresponding to the uid.
13845976Sbostic 	 */
13945976Sbostic 	uid = getuid();
14046034Sbostic 	if (!from && (!(from = getlogin()) ||
14146034Sbostic 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
14245976Sbostic 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
143579Sroot 
14459601Sbostic 	/*
14559601Sbostic 	 * There is no way to distinguish the error status of one delivery
14659601Sbostic 	 * from the rest of the deliveries.  So, if we failed hard on one
14759601Sbostic 	 * or more deliveries, but had no failures on any of the others, we
14859601Sbostic 	 * return a hard failure.  If we failed temporarily on one or more
14959601Sbostic 	 * deliveries, we return a temporary failure regardless of the other
15059601Sbostic 	 * failures.  This results in the delivery being reattempted later
15159601Sbostic 	 * at the expense of repeated failures and multiple deliveries.
15259601Sbostic 	 */
15359601Sbostic 	for (fd = store(from); *argv; ++argv)
15459601Sbostic 		deliver(fd, *argv);
15545976Sbostic 	exit(eval);
156579Sroot }
157579Sroot 
15857648Sbostic int
15945976Sbostic store(from)
16045976Sbostic 	char *from;
161579Sroot {
16245976Sbostic 	FILE *fp;
16345976Sbostic 	time_t tval;
16445976Sbostic 	int fd, eline;
165*67830Seric 	char line[2048];
166*67830Seric 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
167579Sroot 
168*67830Seric 	strcpy(tmpbuf, _PATH_LOCTMP);
169*67830Seric 	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
17059601Sbostic 		e_to_sys(errno);
17159601Sbostic 		err("unable to open temporary file");
17259601Sbostic 	}
173*67830Seric 	(void)unlink(tmpbuf);
174579Sroot 
17545976Sbostic 	(void)time(&tval);
17645976Sbostic 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
177579Sroot 
17845976Sbostic 	line[0] = '\0';
17945976Sbostic 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
18045976Sbostic 		if (line[0] == '\n')
18145976Sbostic 			eline = 1;
18245976Sbostic 		else {
18360097Sbostic 			if (eline && line[0] == 'F' &&
18460097Sbostic 			    !memcmp(line, "From ", 5))
18545976Sbostic 				(void)putc('>', fp);
18645976Sbostic 			eline = 0;
18745976Sbostic 		}
18845976Sbostic 		(void)fprintf(fp, "%s", line);
18959601Sbostic 		if (ferror(fp)) {
19059601Sbostic 			e_to_sys(errno);
19159601Sbostic 			err("temporary file write error");
19259601Sbostic 		}
193579Sroot 	}
194579Sroot 
19545976Sbostic 	/* If message not newline terminated, need an extra. */
19659601Sbostic 	if (!strchr(line, '\n'))
19745976Sbostic 		(void)putc('\n', fp);
19845976Sbostic 	/* Output a newline; note, empty messages are allowed. */
19945976Sbostic 	(void)putc('\n', fp);
20011893Seric 
20159601Sbostic 	if (fflush(fp) == EOF || ferror(fp)) {
20259601Sbostic 		e_to_sys(errno);
20359601Sbostic 		err("temporary file write error");
20459601Sbostic 	}
20559601Sbostic 	return (fd);
206579Sroot }
207579Sroot 
20859601Sbostic void
20945976Sbostic deliver(fd, name)
21045976Sbostic 	int fd;
21145976Sbostic 	char *name;
212579Sroot {
21366735Sbostic 	struct stat fsb, sb;
21445976Sbostic 	struct passwd *pw;
21559601Sbostic 	int mbfd, nr, nw, off;
21645976Sbostic 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
21754203Sbostic 	off_t curoff;
218579Sroot 
21945976Sbostic 	/*
22045976Sbostic 	 * Disallow delivery to unknown names -- special mailboxes can be
22145976Sbostic 	 * handled in the sendmail aliases file.
22245976Sbostic 	 */
22345976Sbostic 	if (!(pw = getpwnam(name))) {
22459601Sbostic 		if (eval != EX_TEMPFAIL)
22559601Sbostic 			eval = EX_UNAVAILABLE;
22659601Sbostic 		warn("unknown name: %s", name);
22759601Sbostic 		return;
22845976Sbostic 	}
229579Sroot 
23057648Sbostic 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
231579Sroot 
232579Sroot 	/*
23366733Sbostic 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
23466733Sbostic 	 * race here, that the file was replaced with a symbolic link after
23566735Sbostic 	 * the lstat returned, but before the open.  We attempt to detect
23666735Sbostic 	 * this by comparing the original stat information and information
23766735Sbostic 	 * returned by an fstat of the file descriptor returned by the open.
23859601Sbostic 	 *
23966735Sbostic 	 * NB: this is a symptom of a larger problem, that the mail spooling
24066735Sbostic 	 * directory is writeable by the wrong users.  If that directory is
24166735Sbostic 	 * writeable, system security is compromised for other reasons, and
24266735Sbostic 	 * it cannot be fixed here.
24366735Sbostic 	 *
24459601Sbostic 	 * If we created the mailbox, set the owner/group.  If that fails,
24559601Sbostic 	 * just return.  Another process may have already opened it, so we
24659601Sbostic 	 * can't unlink it.  Historically, binmail set the owner/group at
24759601Sbostic 	 * each mail delivery.  We no longer do this, assuming that if the
24859601Sbostic 	 * ownership or permissions were changed there was a reason.
24959601Sbostic 	 *
25059601Sbostic 	 * XXX
25159601Sbostic 	 * open(2) should support flock'ing the file.
252579Sroot 	 */
25366736Sbostic tryagain:
25459601Sbostic 	if (lstat(path, &sb)) {
25566736Sbostic 		mbfd = open(path,
25666736Sbostic 		    O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
25766736Sbostic 		if (mbfd == -1) {
25866736Sbostic 			if (errno == EEXIST)
25966736Sbostic 				goto tryagain;
26066736Sbostic 		} else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
26159601Sbostic 			e_to_sys(errno);
26259601Sbostic 			warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
26359601Sbostic 			return;
26459601Sbostic 		}
26559601Sbostic 	} else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) {
26659601Sbostic 		e_to_sys(errno);
26759601Sbostic 		warn("%s: linked file", path);
26859601Sbostic 		return;
26966733Sbostic 	} else {
27059601Sbostic 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
27166735Sbostic 		if (mbfd != -1 &&
27266735Sbostic 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
27366735Sbostic 		    S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
27466735Sbostic 		    sb.st_ino != fsb.st_ino)) {
27566735Sbostic 			warn("%s: file changed after open", path);
27666733Sbostic 			(void)close(mbfd);
27766733Sbostic 			return;
27866733Sbostic 		}
27966733Sbostic 	}
28059601Sbostic 
28159601Sbostic 	if (mbfd == -1) {
28259601Sbostic 		e_to_sys(errno);
28359601Sbostic 		warn("%s: %s", path, strerror(errno));
28459601Sbostic 		return;
28545976Sbostic 	}
286579Sroot 
28759601Sbostic 	/* Wait until we can get a lock on the file. */
28845976Sbostic 	if (flock(mbfd, LOCK_EX)) {
28959601Sbostic 		e_to_sys(errno);
29059601Sbostic 		warn("%s: %s", path, strerror(errno));
29159601Sbostic 		goto err1;
29245976Sbostic 	}
293579Sroot 
29459601Sbostic 	/* Get the starting offset of the new message for biff. */
29554203Sbostic 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
29657648Sbostic 	(void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff);
29759601Sbostic 
29859601Sbostic 	/* Copy the message into the file. */
29954203Sbostic 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
30059601Sbostic 		e_to_sys(errno);
30159601Sbostic 		warn("temporary file: %s", strerror(errno));
30259601Sbostic 		goto err1;
30345976Sbostic 	}
30445976Sbostic 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
30545976Sbostic 		for (off = 0; off < nr; nr -= nw, off += nw)
30645976Sbostic 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
30759601Sbostic 				e_to_sys(errno);
30859601Sbostic 				warn("%s: %s", path, strerror(errno));
30959601Sbostic 				goto err2;;
31045976Sbostic 			}
31145976Sbostic 	if (nr < 0) {
31259601Sbostic 		e_to_sys(errno);
31359601Sbostic 		warn("temporary file: %s", strerror(errno));
31459601Sbostic 		goto err2;;
315579Sroot 	}
316579Sroot 
31759601Sbostic 	/* Flush to disk, don't wait for update. */
31859601Sbostic 	if (fsync(mbfd)) {
31959601Sbostic 		e_to_sys(errno);
32059601Sbostic 		warn("%s: %s", path, strerror(errno));
32159601Sbostic err2:		(void)ftruncate(mbfd, curoff);
32259601Sbostic err1:		(void)close(mbfd);
32359601Sbostic 		return;
32459601Sbostic 	}
32559601Sbostic 
32659601Sbostic 	/* Close and check -- NFS doesn't write until the close. */
32759601Sbostic 	if (close(mbfd)) {
32859601Sbostic 		e_to_sys(errno);
32959601Sbostic 		warn("%s: %s", path, strerror(errno));
33059601Sbostic 		return;
33159601Sbostic 	}
332579Sroot 
33359601Sbostic 	notifybiff(biffmsg);
334579Sroot }
335579Sroot 
33650092Sbostic void
33716731Sralph notifybiff(msg)
33816731Sralph 	char *msg;
33916731Sralph {
34016731Sralph 	static struct sockaddr_in addr;
34116731Sralph 	static int f = -1;
34245976Sbostic 	struct hostent *hp;
34345976Sbostic 	struct servent *sp;
34445976Sbostic 	int len;
34516731Sralph 
34645976Sbostic 	if (!addr.sin_family) {
34745976Sbostic 		/* Be silent if biff service not available. */
34845976Sbostic 		if (!(sp = getservbyname("biff", "udp")))
34945976Sbostic 			return;
35045976Sbostic 		if (!(hp = gethostbyname("localhost"))) {
35159601Sbostic 			warn("localhost: %s", strerror(errno));
35245976Sbostic 			return;
35316731Sralph 		}
35445976Sbostic 		addr.sin_family = hp->h_addrtype;
355*67830Seric 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
35645976Sbostic 		addr.sin_port = sp->s_port;
35716731Sralph 	}
35845976Sbostic 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
35959601Sbostic 		warn("socket: %s", strerror(errno));
36045976Sbostic 		return;
36116731Sralph 	}
36245976Sbostic 	len = strlen(msg) + 1;
36346672Sbostic 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
36446672Sbostic 	    != len)
36559601Sbostic 		warn("sendto biff: %s", strerror(errno));
36616731Sralph }
36716731Sralph 
36850092Sbostic void
36945976Sbostic usage()
370579Sroot {
37159601Sbostic 	eval = EX_USAGE;
37259601Sbostic 	err("usage: mail.local [-f from] user ...");
373579Sroot }
374579Sroot 
37550092Sbostic #if __STDC__
37665558Sbostic void
37759601Sbostic err(const char *fmt, ...)
37850092Sbostic #else
37964993Smckusick void
38059601Sbostic err(fmt, va_alist)
38159601Sbostic 	const char *fmt;
38259601Sbostic 	va_dcl
38350092Sbostic #endif
38459601Sbostic {
38559601Sbostic 	va_list ap;
38650092Sbostic 
38759601Sbostic #if __STDC__
38859601Sbostic 	va_start(ap, fmt);
38959601Sbostic #else
39059601Sbostic 	va_start(ap);
39159601Sbostic #endif
39259601Sbostic 	vwarn(fmt, ap);
39359601Sbostic 	va_end(ap);
39459601Sbostic 
39559601Sbostic 	exit(eval);
39659601Sbostic }
39759601Sbostic 
39850092Sbostic void
39950092Sbostic #if __STDC__
40059601Sbostic warn(const char *fmt, ...)
40150092Sbostic #else
40259601Sbostic warn(fmt, va_alist)
40359601Sbostic 	const char *fmt;
40450092Sbostic 	va_dcl
40550092Sbostic #endif
406579Sroot {
40745976Sbostic 	va_list ap;
40859601Sbostic 
40950092Sbostic #if __STDC__
41046554Sbostic 	va_start(ap, fmt);
41150092Sbostic #else
41250092Sbostic 	va_start(ap);
41350092Sbostic #endif
41459601Sbostic 	vwarn(fmt, ap);
41559601Sbostic 	va_end(ap);
41659601Sbostic }
41759601Sbostic 
41859601Sbostic void
41959601Sbostic vwarn(fmt, ap)
42059601Sbostic 	const char *fmt;
42159601Sbostic 	_BSD_VA_LIST_ ap;
42259601Sbostic {
42358400Sbostic 	/*
42459601Sbostic 	 * Log the message to stderr.
42559601Sbostic 	 *
42659601Sbostic 	 * Don't use LOG_PERROR as an openlog() flag to do this,
42759601Sbostic 	 * it's not portable enough.
42858400Sbostic 	 */
42959601Sbostic 	if (eval != EX_USAGE)
43059601Sbostic 		(void)fprintf(stderr, "mail.local: ");
43158396Sbostic 	(void)vfprintf(stderr, fmt, ap);
43258400Sbostic 	(void)fprintf(stderr, "\n");
43359601Sbostic 
434*67830Seric #ifndef ultrix
43559601Sbostic 	/* Log the message to syslog. */
43645976Sbostic 	vsyslog(LOG_ERR, fmt, ap);
437*67830Seric #else
438*67830Seric 	{
439*67830Seric 		char fmtbuf[10240];
440*67830Seric 
441*67830Seric 		(void) sprintf(fmtbuf, fmt, ap);
442*67830Seric 		syslog(LOG_ERR, "%s", fmtbuf);
443*67830Seric 	}
444*67830Seric #endif
445579Sroot }
44659601Sbostic 
44759601Sbostic /*
44859601Sbostic  * e_to_sys --
44959601Sbostic  *	Guess which errno's are temporary.  Gag me.
45059601Sbostic  */
45159601Sbostic void
45259601Sbostic e_to_sys(num)
45359601Sbostic 	int num;
45459601Sbostic {
45559601Sbostic 	/* Temporary failures override hard errors. */
45659601Sbostic 	if (eval == EX_TEMPFAIL)
45759601Sbostic 		return;
45859601Sbostic 
45959601Sbostic 	switch(num) {		/* Hopefully temporary errors. */
46059601Sbostic #ifdef EAGAIN
46159601Sbostic 	case EAGAIN:		/* Resource temporarily unavailable */
46259601Sbostic #endif
46359601Sbostic #ifdef EDQUOT
46459601Sbostic 	case EDQUOT:		/* Disc quota exceeded */
46559601Sbostic #endif
46659601Sbostic #ifdef EBUSY
46759601Sbostic 	case EBUSY:		/* Device busy */
46859601Sbostic #endif
46959601Sbostic #ifdef EPROCLIM
47059601Sbostic 	case EPROCLIM:		/* Too many processes */
47159601Sbostic #endif
47259601Sbostic #ifdef EUSERS
47359601Sbostic 	case EUSERS:		/* Too many users */
47459601Sbostic #endif
47559601Sbostic #ifdef ECONNABORTED
47659601Sbostic 	case ECONNABORTED:	/* Software caused connection abort */
47759601Sbostic #endif
47859601Sbostic #ifdef ECONNREFUSED
47959601Sbostic 	case ECONNREFUSED:	/* Connection refused */
48059601Sbostic #endif
48159601Sbostic #ifdef ECONNRESET
48259601Sbostic 	case ECONNRESET:	/* Connection reset by peer */
48359601Sbostic #endif
48459601Sbostic #ifdef EDEADLK
48559601Sbostic 	case EDEADLK:		/* Resource deadlock avoided */
48659601Sbostic #endif
48759601Sbostic #ifdef EFBIG
48859601Sbostic 	case EFBIG:		/* File too large */
48959601Sbostic #endif
49059601Sbostic #ifdef EHOSTDOWN
49159601Sbostic 	case EHOSTDOWN:		/* Host is down */
49259601Sbostic #endif
49359601Sbostic #ifdef EHOSTUNREACH
49459601Sbostic 	case EHOSTUNREACH:	/* No route to host */
49559601Sbostic #endif
49659601Sbostic #ifdef EMFILE
49759601Sbostic 	case EMFILE:		/* Too many open files */
49859601Sbostic #endif
49959601Sbostic #ifdef ENETDOWN
50059601Sbostic 	case ENETDOWN:		/* Network is down */
50159601Sbostic #endif
50259601Sbostic #ifdef ENETRESET
50359601Sbostic 	case ENETRESET:		/* Network dropped connection on reset */
50459601Sbostic #endif
50559601Sbostic #ifdef ENETUNREACH
50659601Sbostic 	case ENETUNREACH:	/* Network is unreachable */
50759601Sbostic #endif
50859601Sbostic #ifdef ENFILE
50959601Sbostic 	case ENFILE:		/* Too many open files in system */
51059601Sbostic #endif
51159601Sbostic #ifdef ENOBUFS
51259601Sbostic 	case ENOBUFS:		/* No buffer space available */
51359601Sbostic #endif
51459601Sbostic #ifdef ENOMEM
51559601Sbostic 	case ENOMEM:		/* Cannot allocate memory */
51659601Sbostic #endif
51759601Sbostic #ifdef ENOSPC
51859601Sbostic 	case ENOSPC:		/* No space left on device */
51959601Sbostic #endif
52059601Sbostic #ifdef EROFS
52159601Sbostic 	case EROFS:		/* Read-only file system */
52259601Sbostic #endif
52359601Sbostic #ifdef ESTALE
52459601Sbostic 	case ESTALE:		/* Stale NFS file handle */
52559601Sbostic #endif
52659601Sbostic #ifdef ETIMEDOUT
52759601Sbostic 	case ETIMEDOUT:		/* Connection timed out */
52859601Sbostic #endif
529*67830Seric #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
53059601Sbostic 	case EWOULDBLOCK:	/* Operation would block. */
53159601Sbostic #endif
53259601Sbostic 		eval = EX_TEMPFAIL;
53359601Sbostic 		break;
53459601Sbostic 	default:
53559601Sbostic 		eval = EX_UNAVAILABLE;
53659601Sbostic 		break;
53759601Sbostic 	}
53859601Sbostic }
539*67830Seric 
540*67830Seric #ifndef BSD4_4
541*67830Seric 
542*67830Seric char *
543*67830Seric strerror(eno)
544*67830Seric 	int eno;
545*67830Seric {
546*67830Seric 	extern int sys_nerr;
547*67830Seric 	extern char *sys_errlist[];
548*67830Seric 	static char ebuf[60];
549*67830Seric 
550*67830Seric 	if (eno >= 0 && eno <= sys_nerr)
551*67830Seric 		return sys_errlist[eno];
552*67830Seric 	(void) sprintf(ebuf, "Error %d", eno);
553*67830Seric 	return ebuf;
554*67830Seric }
555*67830Seric 
556*67830Seric #if __STDC__
557*67830Seric snprintf(char *buf, int bufsiz, const char *fmt, ...)
558*67830Seric #else
559*67830Seric snprintf(buf, bufsiz, fmt, va_alist)
560*67830Seric 	char *buf;
561*67830Seric 	int bufsiz;
562*67830Seric 	const char *fmt;
563*67830Seric 	va_dcl
564*67830Seric #endif
565*67830Seric {
566*67830Seric 	va_list ap;
567*67830Seric 
568*67830Seric #if __STDC__
569*67830Seric 	va_start(ap, fmt);
570*67830Seric #else
571*67830Seric 	va_start(ap);
572*67830Seric #endif
573*67830Seric 	vsprintf(buf, fmt, ap);
574*67830Seric 	va_end(ap);
575*67830Seric }
576*67830Seric 
577*67830Seric #endif
578*67830Seric 
579*67830Seric #ifdef ultrix
580*67830Seric 
581*67830Seric int
582*67830Seric mkstemp(template)
583*67830Seric 	char *template;
584*67830Seric {
585*67830Seric 	int fd;
586*67830Seric 
587*67830Seric 	return open(mktemp(template), O_RDWR|O_CREAT|O_EXCL, 0600);
588*67830Seric }
589*67830Seric 
590*67830Seric #endif
591