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*68201Seric static char sccsid[] = "@(#)mail.local.c	8.17 (Berkeley) 01/25/95";
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>
35*68201Seric #include <ctype.h>
3657648Sbostic 
3759601Sbostic #if __STDC__
3859601Sbostic #include <stdarg.h>
3959601Sbostic #else
4059601Sbostic #include <varargs.h>
4159601Sbostic #endif
4259601Sbostic 
4367830Seric #ifndef LOCK_EX
4467830Seric # include <sys/file.h>
4567830Seric #endif
46579Sroot 
4767830Seric #ifdef BSD4_4
4867830Seric # include "pathnames.h"
4967830Seric #endif
5067830Seric 
5167830Seric #ifndef __P
5267830Seric # ifdef __STDC__
5367830Seric #  define __P(protos)	protos
5467830Seric # else
5567830Seric #  define __P(protos)	()
5667830Seric #  define const
5767830Seric # endif
5867830Seric #endif
5967830Seric #ifndef __dead
6067830Seric # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
6167830Seric #  define __dead	__volatile
6267830Seric # else
6367830Seric #  define __dead
6467830Seric # endif
6567830Seric #endif
6667830Seric 
6767830Seric #ifndef BSD4_4
6867830Seric # define _BSD_VA_LIST_	va_list
6967830Seric extern char	*strerror __P((int));
7067830Seric #endif
7167830Seric 
7267830Seric #ifndef _PATH_LOCTMP
7367830Seric # define _PATH_LOCTMP	"/tmp/local.XXXXXX"
7467830Seric #endif
7567830Seric #ifndef _PATH_MAILDIR
7667830Seric # define _PATH_MAILDIR	"/var/spool/mail"
7767830Seric #endif
7867830Seric 
7968188Seric #ifndef S_ISREG
8068188Seric # define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
8167830Seric #endif
8267830Seric 
8359601Sbostic int eval = EX_OK;			/* sysexits.h error value. */
84579Sroot 
8559601Sbostic void		deliver __P((int, char *));
8659601Sbostic void		e_to_sys __P((int));
8759601Sbostic __dead void	err __P((const char *, ...));
8859601Sbostic void		notifybiff __P((char *));
8959601Sbostic int		store __P((char *));
9059601Sbostic void		usage __P((void));
9159601Sbostic void		vwarn __P((const char *, _BSD_VA_LIST_));
9259601Sbostic void		warn __P((const char *, ...));
9350092Sbostic 
9457648Sbostic int
95579Sroot main(argc, argv)
9645976Sbostic 	int argc;
9759601Sbostic 	char *argv[];
98579Sroot {
9945976Sbostic 	struct passwd *pw;
10059601Sbostic 	int ch, fd;
10145976Sbostic 	uid_t uid;
10245976Sbostic 	char *from;
10367830Seric 	extern char *optarg;
10467830Seric 	extern int optind;
105579Sroot 
10668190Seric 	/* make sure we have some open file descriptors */
10768190Seric 	for (fd = 10; fd < 30; fd++)
10868190Seric 		(void) close(fd);
10968190Seric 
11068190Seric 	/* use a reasonable umask */
11168191Seric 	(void) umask(0077);
11268190Seric 
11367830Seric #ifdef LOG_MAIL
11458400Sbostic 	openlog("mail.local", 0, LOG_MAIL);
11567830Seric #else
11667830Seric 	openlog("mail.local", 0);
11767830Seric #endif
11816731Sralph 
11945976Sbostic 	from = NULL;
12045976Sbostic 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
12145976Sbostic 		switch(ch) {
12259601Sbostic 		case 'd':		/* Backward compatible. */
12316731Sralph 			break;
12416731Sralph 		case 'f':
12559601Sbostic 		case 'r':		/* Backward compatible. */
12659601Sbostic 			if (from != NULL) {
12759601Sbostic 				warn("multiple -f options");
12859601Sbostic 				usage();
12959601Sbostic 			}
13045976Sbostic 			from = optarg;
131579Sroot 			break;
13245976Sbostic 		case '?':
13316731Sralph 		default:
13445976Sbostic 			usage();
13516731Sralph 		}
13645976Sbostic 	argc -= optind;
13745976Sbostic 	argv += optind;
138579Sroot 
13945976Sbostic 	if (!*argv)
14045976Sbostic 		usage();
141579Sroot 
14245976Sbostic 	/*
14345976Sbostic 	 * If from not specified, use the name from getlogin() if the
14445976Sbostic 	 * uid matches, otherwise, use the name from the password file
14545976Sbostic 	 * corresponding to the uid.
14645976Sbostic 	 */
14745976Sbostic 	uid = getuid();
14846034Sbostic 	if (!from && (!(from = getlogin()) ||
14946034Sbostic 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
15045976Sbostic 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
151579Sroot 
15259601Sbostic 	/*
15359601Sbostic 	 * There is no way to distinguish the error status of one delivery
15459601Sbostic 	 * from the rest of the deliveries.  So, if we failed hard on one
15559601Sbostic 	 * or more deliveries, but had no failures on any of the others, we
15659601Sbostic 	 * return a hard failure.  If we failed temporarily on one or more
15759601Sbostic 	 * deliveries, we return a temporary failure regardless of the other
15859601Sbostic 	 * failures.  This results in the delivery being reattempted later
15959601Sbostic 	 * at the expense of repeated failures and multiple deliveries.
16059601Sbostic 	 */
16159601Sbostic 	for (fd = store(from); *argv; ++argv)
16259601Sbostic 		deliver(fd, *argv);
16345976Sbostic 	exit(eval);
164579Sroot }
165579Sroot 
16657648Sbostic int
16745976Sbostic store(from)
16845976Sbostic 	char *from;
169579Sroot {
17045976Sbostic 	FILE *fp;
17145976Sbostic 	time_t tval;
17245976Sbostic 	int fd, eline;
17367830Seric 	char line[2048];
17467830Seric 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
175579Sroot 
17667830Seric 	strcpy(tmpbuf, _PATH_LOCTMP);
17767830Seric 	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
17859601Sbostic 		e_to_sys(errno);
17959601Sbostic 		err("unable to open temporary file");
18059601Sbostic 	}
18167830Seric 	(void)unlink(tmpbuf);
182579Sroot 
18345976Sbostic 	(void)time(&tval);
18445976Sbostic 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
185579Sroot 
18645976Sbostic 	line[0] = '\0';
18745976Sbostic 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
18845976Sbostic 		if (line[0] == '\n')
18945976Sbostic 			eline = 1;
19045976Sbostic 		else {
19160097Sbostic 			if (eline && line[0] == 'F' &&
19260097Sbostic 			    !memcmp(line, "From ", 5))
19345976Sbostic 				(void)putc('>', fp);
19445976Sbostic 			eline = 0;
19545976Sbostic 		}
19645976Sbostic 		(void)fprintf(fp, "%s", line);
19759601Sbostic 		if (ferror(fp)) {
19859601Sbostic 			e_to_sys(errno);
19959601Sbostic 			err("temporary file write error");
20059601Sbostic 		}
201579Sroot 	}
202579Sroot 
20345976Sbostic 	/* If message not newline terminated, need an extra. */
20459601Sbostic 	if (!strchr(line, '\n'))
20545976Sbostic 		(void)putc('\n', fp);
20645976Sbostic 	/* Output a newline; note, empty messages are allowed. */
20745976Sbostic 	(void)putc('\n', fp);
20811893Seric 
20959601Sbostic 	if (fflush(fp) == EOF || ferror(fp)) {
21059601Sbostic 		e_to_sys(errno);
21159601Sbostic 		err("temporary file write error");
21259601Sbostic 	}
21359601Sbostic 	return (fd);
214579Sroot }
215579Sroot 
21659601Sbostic void
21745976Sbostic deliver(fd, name)
21845976Sbostic 	int fd;
21945976Sbostic 	char *name;
220579Sroot {
22166735Sbostic 	struct stat fsb, sb;
22245976Sbostic 	struct passwd *pw;
22359601Sbostic 	int mbfd, nr, nw, off;
224*68201Seric 	char *p;
22545976Sbostic 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
22654203Sbostic 	off_t curoff;
227579Sroot 
22845976Sbostic 	/*
22945976Sbostic 	 * Disallow delivery to unknown names -- special mailboxes can be
23045976Sbostic 	 * handled in the sendmail aliases file.
23145976Sbostic 	 */
23245976Sbostic 	if (!(pw = getpwnam(name))) {
23359601Sbostic 		if (eval != EX_TEMPFAIL)
23459601Sbostic 			eval = EX_UNAVAILABLE;
23559601Sbostic 		warn("unknown name: %s", name);
23659601Sbostic 		return;
23745976Sbostic 	}
238579Sroot 
239*68201Seric 	/*
240*68201Seric 	 * Keep name reasonably short to avoid buffer overruns.
241*68201Seric 	 *	This isn't necessary on BSD because of the proper
242*68201Seric 	 *	definition of snprintf(), but it can cause problems
243*68201Seric 	 *	on other systems.
244*68201Seric 	 * Also, clear out any bogus characters.
245*68201Seric 	 */
246*68201Seric 
247*68201Seric 	if (strlen(name) > 40)
248*68201Seric 		name[40] = '\0';
249*68201Seric 	for (p = name; *p != '\0'; p++)
250*68201Seric 	{
251*68201Seric 		if (!isascii(*p))
252*68201Seric 			*p &= 0x7f;
253*68201Seric 		else if (!isprint(*p))
254*68201Seric 			*p = '.';
255*68201Seric 	}
256*68201Seric 
25757648Sbostic 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
258579Sroot 
259579Sroot 	/*
26066733Sbostic 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
26166733Sbostic 	 * race here, that the file was replaced with a symbolic link after
26266735Sbostic 	 * the lstat returned, but before the open.  We attempt to detect
26366735Sbostic 	 * this by comparing the original stat information and information
26466735Sbostic 	 * returned by an fstat of the file descriptor returned by the open.
26559601Sbostic 	 *
26666735Sbostic 	 * NB: this is a symptom of a larger problem, that the mail spooling
26766735Sbostic 	 * directory is writeable by the wrong users.  If that directory is
26866735Sbostic 	 * writeable, system security is compromised for other reasons, and
26966735Sbostic 	 * it cannot be fixed here.
27066735Sbostic 	 *
27159601Sbostic 	 * If we created the mailbox, set the owner/group.  If that fails,
27259601Sbostic 	 * just return.  Another process may have already opened it, so we
27359601Sbostic 	 * can't unlink it.  Historically, binmail set the owner/group at
27459601Sbostic 	 * each mail delivery.  We no longer do this, assuming that if the
27559601Sbostic 	 * ownership or permissions were changed there was a reason.
27659601Sbostic 	 *
27759601Sbostic 	 * XXX
27859601Sbostic 	 * open(2) should support flock'ing the file.
279579Sroot 	 */
28066736Sbostic tryagain:
28168005Seric 	lockmbox(path);
28259601Sbostic 	if (lstat(path, &sb)) {
28366736Sbostic 		mbfd = open(path,
28466736Sbostic 		    O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
28566736Sbostic 		if (mbfd == -1) {
28666736Sbostic 			if (errno == EEXIST)
28766736Sbostic 				goto tryagain;
28866736Sbostic 		} else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
28959601Sbostic 			e_to_sys(errno);
29059601Sbostic 			warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
29168005Seric 			unlockmbox();
29259601Sbostic 			return;
29359601Sbostic 		}
29468188Seric 	} else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
29559601Sbostic 		e_to_sys(errno);
29668188Seric 		warn("%s: irregular file", path);
29768005Seric 		unlockmbox();
29859601Sbostic 		return;
29968060Seric 	} else if (sb.st_uid != pw->pw_uid) {
30068060Seric 		warn("%s: wrong ownership (%d)", path, sb.st_uid);
30168060Seric 		unlockmbox();
30268060Seric 		return;
30366733Sbostic 	} else {
30459601Sbostic 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
30566735Sbostic 		if (mbfd != -1 &&
30666735Sbostic 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
30768188Seric 		    !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
30868188Seric 		    sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
30966735Sbostic 			warn("%s: file changed after open", path);
31066733Sbostic 			(void)close(mbfd);
31168005Seric 			unlockmbox();
31266733Sbostic 			return;
31366733Sbostic 		}
31466733Sbostic 	}
31559601Sbostic 
31659601Sbostic 	if (mbfd == -1) {
31759601Sbostic 		e_to_sys(errno);
31859601Sbostic 		warn("%s: %s", path, strerror(errno));
31968005Seric 		unlockmbox();
32059601Sbostic 		return;
32145976Sbostic 	}
322579Sroot 
32359601Sbostic 	/* Wait until we can get a lock on the file. */
32445976Sbostic 	if (flock(mbfd, LOCK_EX)) {
32559601Sbostic 		e_to_sys(errno);
32659601Sbostic 		warn("%s: %s", path, strerror(errno));
32768005Seric 		unlockmbox();
32859601Sbostic 		goto err1;
32945976Sbostic 	}
330579Sroot 
33159601Sbostic 	/* Get the starting offset of the new message for biff. */
33254203Sbostic 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
33368021Seric 	(void)snprintf(biffmsg, sizeof(biffmsg),
33468021Seric 		sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
33568021Seric 		name, curoff);
33659601Sbostic 
33759601Sbostic 	/* Copy the message into the file. */
33854203Sbostic 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
33959601Sbostic 		e_to_sys(errno);
34059601Sbostic 		warn("temporary file: %s", strerror(errno));
34159601Sbostic 		goto err1;
34245976Sbostic 	}
34345976Sbostic 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
344*68201Seric 		for (off = 0; off < nr; off += nw)
345*68201Seric 			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
34659601Sbostic 				e_to_sys(errno);
34759601Sbostic 				warn("%s: %s", path, strerror(errno));
34859601Sbostic 				goto err2;;
34945976Sbostic 			}
35045976Sbostic 	if (nr < 0) {
35159601Sbostic 		e_to_sys(errno);
35259601Sbostic 		warn("temporary file: %s", strerror(errno));
35359601Sbostic 		goto err2;;
354579Sroot 	}
355579Sroot 
35659601Sbostic 	/* Flush to disk, don't wait for update. */
35759601Sbostic 	if (fsync(mbfd)) {
35859601Sbostic 		e_to_sys(errno);
35959601Sbostic 		warn("%s: %s", path, strerror(errno));
36059601Sbostic err2:		(void)ftruncate(mbfd, curoff);
36159601Sbostic err1:		(void)close(mbfd);
36268005Seric 		unlockmbox();
36359601Sbostic 		return;
36459601Sbostic 	}
36559601Sbostic 
36659601Sbostic 	/* Close and check -- NFS doesn't write until the close. */
36759601Sbostic 	if (close(mbfd)) {
36859601Sbostic 		e_to_sys(errno);
36959601Sbostic 		warn("%s: %s", path, strerror(errno));
37068005Seric 		unlockmbox();
37159601Sbostic 		return;
37259601Sbostic 	}
373579Sroot 
37468005Seric 	unlockmbox();
37559601Sbostic 	notifybiff(biffmsg);
376579Sroot }
377579Sroot 
37868005Seric /*
37968005Seric  * user.lock files are necessary for compatibility with other
38068005Seric  * systems, e.g., when the mail spool file is NFS exported.
38168005Seric  * Alas, mailbox locking is more than just a local matter.
38268005Seric  * EPA 11/94.
38368005Seric  */
38468005Seric 
385*68201Seric char	lockname[MAXPATHLEN];
38668005Seric int	locked = 0;
38768005Seric 
38868005Seric lockmbox(path)
38968005Seric 	char *path;
39068005Seric {
39168005Seric 	int statfailed = 0;
39268005Seric 
39368005Seric 	if (locked)
39468005Seric 		return;
39568005Seric 	sprintf(lockname, "%s.lock", path);
39668005Seric 	for (;; sleep(5)) {
39768005Seric 		int fd;
39868005Seric 		struct stat st;
39968005Seric 		time_t now;
40068005Seric 
40168187Seric 		fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
40268187Seric 		if (fd >= 0) {
40368005Seric 			locked = 1;
40468187Seric 			close(fd);
40568005Seric 			return;
40668005Seric 		}
40768005Seric 		if (stat(lockname, &st) < 0) {
40868005Seric 			if (statfailed++ > 5)
40968005Seric 				return;
41068005Seric 			continue;
41168005Seric 		}
41268005Seric 		statfailed = 0;
41368005Seric 		time(&now);
41468005Seric 		if (now < st.st_ctime + 300)
41568005Seric 			continue;
41668005Seric 		unlink(lockname);
41768005Seric 	}
41868005Seric }
41968005Seric 
42068005Seric unlockmbox()
42168005Seric {
42268005Seric 	if (!locked)
42368005Seric 		return;
42468005Seric 	unlink(lockname);
42568005Seric 	locked = 0;
42668005Seric }
42768005Seric 
42850092Sbostic void
42916731Sralph notifybiff(msg)
43016731Sralph 	char *msg;
43116731Sralph {
43216731Sralph 	static struct sockaddr_in addr;
43316731Sralph 	static int f = -1;
43445976Sbostic 	struct hostent *hp;
43545976Sbostic 	struct servent *sp;
43645976Sbostic 	int len;
43716731Sralph 
43845976Sbostic 	if (!addr.sin_family) {
43945976Sbostic 		/* Be silent if biff service not available. */
44045976Sbostic 		if (!(sp = getservbyname("biff", "udp")))
44145976Sbostic 			return;
44245976Sbostic 		if (!(hp = gethostbyname("localhost"))) {
44359601Sbostic 			warn("localhost: %s", strerror(errno));
44445976Sbostic 			return;
44516731Sralph 		}
44645976Sbostic 		addr.sin_family = hp->h_addrtype;
44767830Seric 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
44845976Sbostic 		addr.sin_port = sp->s_port;
44916731Sralph 	}
45045976Sbostic 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
45159601Sbostic 		warn("socket: %s", strerror(errno));
45245976Sbostic 		return;
45316731Sralph 	}
45445976Sbostic 	len = strlen(msg) + 1;
45546672Sbostic 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
45646672Sbostic 	    != len)
45759601Sbostic 		warn("sendto biff: %s", strerror(errno));
45816731Sralph }
45916731Sralph 
46050092Sbostic void
46145976Sbostic usage()
462579Sroot {
46359601Sbostic 	eval = EX_USAGE;
46459601Sbostic 	err("usage: mail.local [-f from] user ...");
465579Sroot }
466579Sroot 
46750092Sbostic #if __STDC__
46865558Sbostic void
46959601Sbostic err(const char *fmt, ...)
47050092Sbostic #else
47164993Smckusick void
47259601Sbostic err(fmt, va_alist)
47359601Sbostic 	const char *fmt;
47459601Sbostic 	va_dcl
47550092Sbostic #endif
47659601Sbostic {
47759601Sbostic 	va_list ap;
47850092Sbostic 
47959601Sbostic #if __STDC__
48059601Sbostic 	va_start(ap, fmt);
48159601Sbostic #else
48259601Sbostic 	va_start(ap);
48359601Sbostic #endif
48459601Sbostic 	vwarn(fmt, ap);
48559601Sbostic 	va_end(ap);
48659601Sbostic 
48759601Sbostic 	exit(eval);
48859601Sbostic }
48959601Sbostic 
49050092Sbostic void
49150092Sbostic #if __STDC__
49259601Sbostic warn(const char *fmt, ...)
49350092Sbostic #else
49459601Sbostic warn(fmt, va_alist)
49559601Sbostic 	const char *fmt;
49650092Sbostic 	va_dcl
49750092Sbostic #endif
498579Sroot {
49945976Sbostic 	va_list ap;
50059601Sbostic 
50150092Sbostic #if __STDC__
50246554Sbostic 	va_start(ap, fmt);
50350092Sbostic #else
50450092Sbostic 	va_start(ap);
50550092Sbostic #endif
50659601Sbostic 	vwarn(fmt, ap);
50759601Sbostic 	va_end(ap);
50859601Sbostic }
50959601Sbostic 
51059601Sbostic void
51159601Sbostic vwarn(fmt, ap)
51259601Sbostic 	const char *fmt;
51359601Sbostic 	_BSD_VA_LIST_ ap;
51459601Sbostic {
51558400Sbostic 	/*
51659601Sbostic 	 * Log the message to stderr.
51759601Sbostic 	 *
51859601Sbostic 	 * Don't use LOG_PERROR as an openlog() flag to do this,
51959601Sbostic 	 * it's not portable enough.
52058400Sbostic 	 */
52159601Sbostic 	if (eval != EX_USAGE)
52259601Sbostic 		(void)fprintf(stderr, "mail.local: ");
52358396Sbostic 	(void)vfprintf(stderr, fmt, ap);
52458400Sbostic 	(void)fprintf(stderr, "\n");
52559601Sbostic 
52667830Seric #ifndef ultrix
52759601Sbostic 	/* Log the message to syslog. */
52845976Sbostic 	vsyslog(LOG_ERR, fmt, ap);
52967830Seric #else
53067830Seric 	{
53167830Seric 		char fmtbuf[10240];
53267830Seric 
53367830Seric 		(void) sprintf(fmtbuf, fmt, ap);
53467830Seric 		syslog(LOG_ERR, "%s", fmtbuf);
53567830Seric 	}
53667830Seric #endif
537579Sroot }
53859601Sbostic 
53959601Sbostic /*
54059601Sbostic  * e_to_sys --
54159601Sbostic  *	Guess which errno's are temporary.  Gag me.
54259601Sbostic  */
54359601Sbostic void
54459601Sbostic e_to_sys(num)
54559601Sbostic 	int num;
54659601Sbostic {
54759601Sbostic 	/* Temporary failures override hard errors. */
54859601Sbostic 	if (eval == EX_TEMPFAIL)
54959601Sbostic 		return;
55059601Sbostic 
55159601Sbostic 	switch(num) {		/* Hopefully temporary errors. */
55259601Sbostic #ifdef EAGAIN
55359601Sbostic 	case EAGAIN:		/* Resource temporarily unavailable */
55459601Sbostic #endif
55559601Sbostic #ifdef EDQUOT
55659601Sbostic 	case EDQUOT:		/* Disc quota exceeded */
55759601Sbostic #endif
55859601Sbostic #ifdef EBUSY
55959601Sbostic 	case EBUSY:		/* Device busy */
56059601Sbostic #endif
56159601Sbostic #ifdef EPROCLIM
56259601Sbostic 	case EPROCLIM:		/* Too many processes */
56359601Sbostic #endif
56459601Sbostic #ifdef EUSERS
56559601Sbostic 	case EUSERS:		/* Too many users */
56659601Sbostic #endif
56759601Sbostic #ifdef ECONNABORTED
56859601Sbostic 	case ECONNABORTED:	/* Software caused connection abort */
56959601Sbostic #endif
57059601Sbostic #ifdef ECONNREFUSED
57159601Sbostic 	case ECONNREFUSED:	/* Connection refused */
57259601Sbostic #endif
57359601Sbostic #ifdef ECONNRESET
57459601Sbostic 	case ECONNRESET:	/* Connection reset by peer */
57559601Sbostic #endif
57659601Sbostic #ifdef EDEADLK
57759601Sbostic 	case EDEADLK:		/* Resource deadlock avoided */
57859601Sbostic #endif
57959601Sbostic #ifdef EFBIG
58059601Sbostic 	case EFBIG:		/* File too large */
58159601Sbostic #endif
58259601Sbostic #ifdef EHOSTDOWN
58359601Sbostic 	case EHOSTDOWN:		/* Host is down */
58459601Sbostic #endif
58559601Sbostic #ifdef EHOSTUNREACH
58659601Sbostic 	case EHOSTUNREACH:	/* No route to host */
58759601Sbostic #endif
58859601Sbostic #ifdef EMFILE
58959601Sbostic 	case EMFILE:		/* Too many open files */
59059601Sbostic #endif
59159601Sbostic #ifdef ENETDOWN
59259601Sbostic 	case ENETDOWN:		/* Network is down */
59359601Sbostic #endif
59459601Sbostic #ifdef ENETRESET
59559601Sbostic 	case ENETRESET:		/* Network dropped connection on reset */
59659601Sbostic #endif
59759601Sbostic #ifdef ENETUNREACH
59859601Sbostic 	case ENETUNREACH:	/* Network is unreachable */
59959601Sbostic #endif
60059601Sbostic #ifdef ENFILE
60159601Sbostic 	case ENFILE:		/* Too many open files in system */
60259601Sbostic #endif
60359601Sbostic #ifdef ENOBUFS
60459601Sbostic 	case ENOBUFS:		/* No buffer space available */
60559601Sbostic #endif
60659601Sbostic #ifdef ENOMEM
60759601Sbostic 	case ENOMEM:		/* Cannot allocate memory */
60859601Sbostic #endif
60959601Sbostic #ifdef ENOSPC
61059601Sbostic 	case ENOSPC:		/* No space left on device */
61159601Sbostic #endif
61259601Sbostic #ifdef EROFS
61359601Sbostic 	case EROFS:		/* Read-only file system */
61459601Sbostic #endif
61559601Sbostic #ifdef ESTALE
61659601Sbostic 	case ESTALE:		/* Stale NFS file handle */
61759601Sbostic #endif
61859601Sbostic #ifdef ETIMEDOUT
61959601Sbostic 	case ETIMEDOUT:		/* Connection timed out */
62059601Sbostic #endif
62167830Seric #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
62259601Sbostic 	case EWOULDBLOCK:	/* Operation would block. */
62359601Sbostic #endif
62459601Sbostic 		eval = EX_TEMPFAIL;
62559601Sbostic 		break;
62659601Sbostic 	default:
62759601Sbostic 		eval = EX_UNAVAILABLE;
62859601Sbostic 		break;
62959601Sbostic 	}
63059601Sbostic }
63167830Seric 
63267830Seric #ifndef BSD4_4
63367830Seric 
63467830Seric char *
63567830Seric strerror(eno)
63667830Seric 	int eno;
63767830Seric {
63867830Seric 	extern int sys_nerr;
63967830Seric 	extern char *sys_errlist[];
64067830Seric 	static char ebuf[60];
64167830Seric 
64267830Seric 	if (eno >= 0 && eno <= sys_nerr)
64367830Seric 		return sys_errlist[eno];
64467830Seric 	(void) sprintf(ebuf, "Error %d", eno);
64567830Seric 	return ebuf;
64667830Seric }
64767830Seric 
64867830Seric #if __STDC__
64967830Seric snprintf(char *buf, int bufsiz, const char *fmt, ...)
65067830Seric #else
65167830Seric snprintf(buf, bufsiz, fmt, va_alist)
65267830Seric 	char *buf;
65367830Seric 	int bufsiz;
65467830Seric 	const char *fmt;
65567830Seric 	va_dcl
65667830Seric #endif
65767830Seric {
65867830Seric 	va_list ap;
65967830Seric 
66067830Seric #if __STDC__
66167830Seric 	va_start(ap, fmt);
66267830Seric #else
66367830Seric 	va_start(ap);
66467830Seric #endif
66567830Seric 	vsprintf(buf, fmt, ap);
66667830Seric 	va_end(ap);
66767830Seric }
66867830Seric 
66967830Seric #endif
67067830Seric 
67167830Seric #ifdef ultrix
67267830Seric 
67368189Seric /*
67468189Seric  * Copyright (c) 1987, 1993
67568189Seric  *	The Regents of the University of California.  All rights reserved.
67668189Seric  *
67768189Seric  * Redistribution and use in source and binary forms, with or without
67868189Seric  * modification, are permitted provided that the following conditions
67968189Seric  * are met:
68068189Seric  * 1. Redistributions of source code must retain the above copyright
68168189Seric  *    notice, this list of conditions and the following disclaimer.
68268189Seric  * 2. Redistributions in binary form must reproduce the above copyright
68368189Seric  *    notice, this list of conditions and the following disclaimer in the
68468189Seric  *    documentation and/or other materials provided with the distribution.
68568189Seric  * 3. All advertising materials mentioning features or use of this software
68668189Seric  *    must display the following acknowledgement:
68768189Seric  *	This product includes software developed by the University of
68868189Seric  *	California, Berkeley and its contributors.
68968189Seric  * 4. Neither the name of the University nor the names of its contributors
69068189Seric  *    may be used to endorse or promote products derived from this software
69168189Seric  *    without specific prior written permission.
69268189Seric  *
69368189Seric  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
69468189Seric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
69568189Seric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
69668189Seric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
69768189Seric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
69868189Seric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69968189Seric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70068189Seric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70168189Seric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70268189Seric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70368189Seric  * SUCH DAMAGE.
70468189Seric  */
70568189Seric 
70668189Seric #if defined(LIBC_SCCS) && !defined(lint)
70768189Seric static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
70868189Seric #endif /* LIBC_SCCS and not lint */
70968189Seric 
71068189Seric #include <sys/types.h>
71168189Seric #include <sys/stat.h>
71268189Seric #include <fcntl.h>
71368189Seric #include <errno.h>
71468189Seric #include <stdio.h>
71568189Seric #include <ctype.h>
71668189Seric 
71768189Seric static int _gettemp();
71868189Seric 
71968189Seric mkstemp(path)
72068189Seric 	char *path;
72167830Seric {
72267830Seric 	int fd;
72367830Seric 
72468189Seric 	return (_gettemp(path, &fd) ? fd : -1);
72567830Seric }
72667830Seric 
72768189Seric /*
72868189Seric char *
72968189Seric mktemp(path)
73068189Seric 	char *path;
73168189Seric {
73268189Seric 	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
73368189Seric }
73468189Seric */
73568189Seric 
73668189Seric static
73768189Seric _gettemp(path, doopen)
73868189Seric 	char *path;
73968189Seric 	register int *doopen;
74068189Seric {
74168189Seric 	extern int errno;
74268189Seric 	register char *start, *trv;
74368189Seric 	struct stat sbuf;
74468189Seric 	u_int pid;
74568189Seric 
74668189Seric 	pid = getpid();
74768189Seric 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
74868189Seric 	while (*--trv == 'X') {
74968189Seric 		*trv = (pid % 10) + '0';
75068189Seric 		pid /= 10;
75168189Seric 	}
75268189Seric 
75368189Seric 	/*
75468189Seric 	 * check the target directory; if you have six X's and it
75568189Seric 	 * doesn't exist this runs for a *very* long time.
75668189Seric 	 */
75768189Seric 	for (start = trv + 1;; --trv) {
75868189Seric 		if (trv <= path)
75968189Seric 			break;
76068189Seric 		if (*trv == '/') {
76168189Seric 			*trv = '\0';
76268189Seric 			if (stat(path, &sbuf))
76368189Seric 				return(0);
76468189Seric 			if (!S_ISDIR(sbuf.st_mode)) {
76568189Seric 				errno = ENOTDIR;
76668189Seric 				return(0);
76768189Seric 			}
76868189Seric 			*trv = '/';
76968189Seric 			break;
77068189Seric 		}
77168189Seric 	}
77268189Seric 
77368189Seric 	for (;;) {
77468189Seric 		if (doopen) {
77568189Seric 			if ((*doopen =
77668189Seric 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
77768189Seric 				return(1);
77868189Seric 			if (errno != EEXIST)
77968189Seric 				return(0);
78068189Seric 		}
78168189Seric 		else if (stat(path, &sbuf))
78268189Seric 			return(errno == ENOENT ? 1 : 0);
78368189Seric 
78468189Seric 		/* tricky little algorithm for backward compatibility */
78568189Seric 		for (trv = start;;) {
78668189Seric 			if (!*trv)
78768189Seric 				return(0);
78868189Seric 			if (*trv == 'z')
78968189Seric 				*trv++ = 'a';
79068189Seric 			else {
79168189Seric 				if (isdigit(*trv))
79268189Seric 					*trv = 'a';
79368189Seric 				else
79468189Seric 					++*trv;
79568189Seric 				break;
79668189Seric 			}
79768189Seric 		}
79868189Seric 	}
79968189Seric 	/*NOTREACHED*/
80068189Seric }
80168189Seric 
80267830Seric #endif
803