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*69964Seric static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 06/21/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));
7969603Seric extern int snprintf __P((char *, int, const char *, ...));
8067830Seric #endif
8167830Seric
8269603Seric /*
8369604Seric * If you don't have setreuid, and you have saved uids, and you have
8469604Seric * a seteuid() call that doesn't try to emulate using setuid(), then
8569604Seric * you can try defining USE_SETEUID.
8669603Seric */
8769604Seric #ifdef USE_SETEUID
8869604Seric # define setreuid(r, e) seteuid(e)
8969603Seric #endif
9069603Seric
9167830Seric #ifndef _PATH_LOCTMP
9267830Seric # define _PATH_LOCTMP "/tmp/local.XXXXXX"
9367830Seric #endif
9467830Seric #ifndef _PATH_MAILDIR
9567830Seric # define _PATH_MAILDIR "/var/spool/mail"
9667830Seric #endif
9767830Seric
9868188Seric #ifndef S_ISREG
9968188Seric # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
10067830Seric #endif
10167830Seric
10259601Sbostic int eval = EX_OK; /* sysexits.h error value. */
103579Sroot
10459601Sbostic void deliver __P((int, char *));
10559601Sbostic void e_to_sys __P((int));
10659601Sbostic __dead void err __P((const char *, ...));
10759601Sbostic void notifybiff __P((char *));
10859601Sbostic int store __P((char *));
10959601Sbostic void usage __P((void));
11059601Sbostic void vwarn __P((const char *, _BSD_VA_LIST_));
11159601Sbostic void warn __P((const char *, ...));
11250092Sbostic
11357648Sbostic int
main(argc,argv)114579Sroot main(argc, argv)
11545976Sbostic int argc;
11659601Sbostic char *argv[];
117579Sroot {
11845976Sbostic struct passwd *pw;
11959601Sbostic int ch, fd;
12045976Sbostic uid_t uid;
12145976Sbostic char *from;
12267830Seric extern char *optarg;
12367830Seric extern int optind;
124579Sroot
12568190Seric /* make sure we have some open file descriptors */
12668190Seric for (fd = 10; fd < 30; fd++)
12768190Seric (void) close(fd);
12868190Seric
12968190Seric /* use a reasonable umask */
13068191Seric (void) umask(0077);
13168190Seric
13267830Seric #ifdef LOG_MAIL
13358400Sbostic openlog("mail.local", 0, LOG_MAIL);
13467830Seric #else
13567830Seric openlog("mail.local", 0);
13667830Seric #endif
13716731Sralph
13845976Sbostic from = NULL;
13945976Sbostic while ((ch = getopt(argc, argv, "df:r:")) != EOF)
14045976Sbostic switch(ch) {
14159601Sbostic case 'd': /* Backward compatible. */
14216731Sralph break;
14316731Sralph case 'f':
14459601Sbostic case 'r': /* Backward compatible. */
14559601Sbostic if (from != NULL) {
14659601Sbostic warn("multiple -f options");
14759601Sbostic usage();
14859601Sbostic }
14945976Sbostic from = optarg;
150579Sroot break;
15145976Sbostic case '?':
15216731Sralph default:
15345976Sbostic usage();
15416731Sralph }
15545976Sbostic argc -= optind;
15645976Sbostic argv += optind;
157579Sroot
15845976Sbostic if (!*argv)
15945976Sbostic usage();
160579Sroot
16145976Sbostic /*
16245976Sbostic * If from not specified, use the name from getlogin() if the
16345976Sbostic * uid matches, otherwise, use the name from the password file
16445976Sbostic * corresponding to the uid.
16545976Sbostic */
16645976Sbostic uid = getuid();
16746034Sbostic if (!from && (!(from = getlogin()) ||
16846034Sbostic !(pw = getpwnam(from)) || pw->pw_uid != uid))
16945976Sbostic from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
170579Sroot
17159601Sbostic /*
17259601Sbostic * There is no way to distinguish the error status of one delivery
17359601Sbostic * from the rest of the deliveries. So, if we failed hard on one
17459601Sbostic * or more deliveries, but had no failures on any of the others, we
17559601Sbostic * return a hard failure. If we failed temporarily on one or more
17659601Sbostic * deliveries, we return a temporary failure regardless of the other
17759601Sbostic * failures. This results in the delivery being reattempted later
17859601Sbostic * at the expense of repeated failures and multiple deliveries.
17959601Sbostic */
18059601Sbostic for (fd = store(from); *argv; ++argv)
18159601Sbostic deliver(fd, *argv);
18245976Sbostic exit(eval);
183579Sroot }
184579Sroot
18557648Sbostic int
store(from)18645976Sbostic store(from)
18745976Sbostic char *from;
188579Sroot {
18945976Sbostic FILE *fp;
19045976Sbostic time_t tval;
19145976Sbostic int fd, eline;
19267830Seric char line[2048];
19367830Seric char tmpbuf[sizeof _PATH_LOCTMP + 1];
194579Sroot
19567830Seric strcpy(tmpbuf, _PATH_LOCTMP);
19667830Seric if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
19759601Sbostic e_to_sys(errno);
19859601Sbostic err("unable to open temporary file");
19959601Sbostic }
20067830Seric (void)unlink(tmpbuf);
201579Sroot
20245976Sbostic (void)time(&tval);
20345976Sbostic (void)fprintf(fp, "From %s %s", from, ctime(&tval));
204579Sroot
20545976Sbostic line[0] = '\0';
20645976Sbostic for (eline = 1; fgets(line, sizeof(line), stdin);) {
20745976Sbostic if (line[0] == '\n')
20845976Sbostic eline = 1;
20945976Sbostic else {
21060097Sbostic if (eline && line[0] == 'F' &&
21160097Sbostic !memcmp(line, "From ", 5))
21245976Sbostic (void)putc('>', fp);
21345976Sbostic eline = 0;
21445976Sbostic }
21545976Sbostic (void)fprintf(fp, "%s", line);
21659601Sbostic if (ferror(fp)) {
21759601Sbostic e_to_sys(errno);
21859601Sbostic err("temporary file write error");
21959601Sbostic }
220579Sroot }
221579Sroot
22245976Sbostic /* If message not newline terminated, need an extra. */
22359601Sbostic if (!strchr(line, '\n'))
22445976Sbostic (void)putc('\n', fp);
22545976Sbostic /* Output a newline; note, empty messages are allowed. */
22645976Sbostic (void)putc('\n', fp);
22711893Seric
22859601Sbostic if (fflush(fp) == EOF || ferror(fp)) {
22959601Sbostic e_to_sys(errno);
23059601Sbostic err("temporary file write error");
23159601Sbostic }
23259601Sbostic return (fd);
233579Sroot }
234579Sroot
23559601Sbostic void
deliver(fd,name)23645976Sbostic deliver(fd, name)
23745976Sbostic int fd;
23845976Sbostic char *name;
239579Sroot {
24066735Sbostic struct stat fsb, sb;
24145976Sbostic struct passwd *pw;
24259601Sbostic int mbfd, nr, nw, off;
24368201Seric char *p;
24445976Sbostic char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
24554203Sbostic off_t curoff;
246579Sroot
24745976Sbostic /*
24845976Sbostic * Disallow delivery to unknown names -- special mailboxes can be
24945976Sbostic * handled in the sendmail aliases file.
25045976Sbostic */
25145976Sbostic if (!(pw = getpwnam(name))) {
25259601Sbostic if (eval != EX_TEMPFAIL)
25359601Sbostic eval = EX_UNAVAILABLE;
25459601Sbostic warn("unknown name: %s", name);
25559601Sbostic return;
25645976Sbostic }
257*69964Seric endpwent();
258579Sroot
25968201Seric /*
26068201Seric * Keep name reasonably short to avoid buffer overruns.
26168201Seric * This isn't necessary on BSD because of the proper
26268201Seric * definition of snprintf(), but it can cause problems
26368201Seric * on other systems.
26468201Seric * Also, clear out any bogus characters.
26568201Seric */
26668201Seric
26768201Seric if (strlen(name) > 40)
26868201Seric name[40] = '\0';
26968201Seric for (p = name; *p != '\0'; p++)
27068201Seric {
27168201Seric if (!isascii(*p))
27268201Seric *p &= 0x7f;
27368201Seric else if (!isprint(*p))
27468201Seric *p = '.';
27568201Seric }
27668201Seric
27757648Sbostic (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
278579Sroot
279579Sroot /*
28066733Sbostic * If the mailbox is linked or a symlink, fail. There's an obvious
28166733Sbostic * race here, that the file was replaced with a symbolic link after
28266735Sbostic * the lstat returned, but before the open. We attempt to detect
28366735Sbostic * this by comparing the original stat information and information
28466735Sbostic * returned by an fstat of the file descriptor returned by the open.
28559601Sbostic *
28666735Sbostic * NB: this is a symptom of a larger problem, that the mail spooling
28766735Sbostic * directory is writeable by the wrong users. If that directory is
28866735Sbostic * writeable, system security is compromised for other reasons, and
28966735Sbostic * it cannot be fixed here.
29066735Sbostic *
29159601Sbostic * If we created the mailbox, set the owner/group. If that fails,
29259601Sbostic * just return. Another process may have already opened it, so we
29359601Sbostic * can't unlink it. Historically, binmail set the owner/group at
29459601Sbostic * each mail delivery. We no longer do this, assuming that if the
29559601Sbostic * ownership or permissions were changed there was a reason.
29659601Sbostic *
29759601Sbostic * XXX
29859601Sbostic * open(2) should support flock'ing the file.
299579Sroot */
30066736Sbostic tryagain:
30168005Seric lockmbox(path);
30259601Sbostic if (lstat(path, &sb)) {
30366736Sbostic mbfd = open(path,
30466736Sbostic O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
30566736Sbostic if (mbfd == -1) {
30666736Sbostic if (errno == EEXIST)
30766736Sbostic goto tryagain;
30866736Sbostic } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
30959601Sbostic e_to_sys(errno);
31059601Sbostic warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
31169603Seric goto err1;
31259601Sbostic }
31368188Seric } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
31459601Sbostic e_to_sys(errno);
31568188Seric warn("%s: irregular file", path);
31669603Seric goto err0;
31768060Seric } else if (sb.st_uid != pw->pw_uid) {
31868060Seric warn("%s: wrong ownership (%d)", path, sb.st_uid);
31968060Seric unlockmbox();
32068060Seric return;
32166733Sbostic } else {
32259601Sbostic mbfd = open(path, O_APPEND|O_WRONLY, 0);
32366735Sbostic if (mbfd != -1 &&
32466735Sbostic (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
32568188Seric !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
32668188Seric sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
32766735Sbostic warn("%s: file changed after open", path);
32869603Seric goto err1;
32966733Sbostic }
33066733Sbostic }
33159601Sbostic
33259601Sbostic if (mbfd == -1) {
33359601Sbostic e_to_sys(errno);
33459601Sbostic warn("%s: %s", path, strerror(errno));
33569603Seric goto err0;
33645976Sbostic }
337579Sroot
33859601Sbostic /* Wait until we can get a lock on the file. */
33945976Sbostic if (flock(mbfd, LOCK_EX)) {
34059601Sbostic e_to_sys(errno);
34159601Sbostic warn("%s: %s", path, strerror(errno));
34259601Sbostic goto err1;
34345976Sbostic }
344579Sroot
34559601Sbostic /* Get the starting offset of the new message for biff. */
34654203Sbostic curoff = lseek(mbfd, (off_t)0, SEEK_END);
34768021Seric (void)snprintf(biffmsg, sizeof(biffmsg),
34868021Seric sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
34968021Seric name, curoff);
35059601Sbostic
35159601Sbostic /* Copy the message into the file. */
35254203Sbostic if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
35359601Sbostic e_to_sys(errno);
35459601Sbostic warn("temporary file: %s", strerror(errno));
35559601Sbostic goto err1;
35645976Sbostic }
35769603Seric if (setreuid(0, pw->pw_uid) < 0) {
35869603Seric e_to_sys(errno);
35969603Seric warn("setreuid(0, %d): %s (r=%d, e=%d)",
36069603Seric pw->pw_uid, strerror(errno), getuid(), geteuid());
36169603Seric goto err1;
36269603Seric }
36369603Seric #ifdef DEBUG
36469603Seric printf("new euid = %d\n", geteuid());
36569603Seric #endif
36645976Sbostic while ((nr = read(fd, buf, sizeof(buf))) > 0)
36768201Seric for (off = 0; off < nr; off += nw)
36868201Seric if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
36959601Sbostic e_to_sys(errno);
37059601Sbostic warn("%s: %s", path, strerror(errno));
37169603Seric goto err3;
37245976Sbostic }
37345976Sbostic if (nr < 0) {
37459601Sbostic e_to_sys(errno);
37559601Sbostic warn("temporary file: %s", strerror(errno));
37669603Seric goto err3;
377579Sroot }
378579Sroot
37959601Sbostic /* Flush to disk, don't wait for update. */
38059601Sbostic if (fsync(mbfd)) {
38159601Sbostic e_to_sys(errno);
38259601Sbostic warn("%s: %s", path, strerror(errno));
38369603Seric err3:
38469603Seric if (setreuid(0, 0) < 0) {
38569603Seric e_to_sys(errno);
38669603Seric warn("setreuid(0, 0): %s", strerror(errno));
38769603Seric }
38869604Seric #ifdef DEBUG
38969603Seric printf("reset euid = %d\n", geteuid());
39069603Seric #endif
39159601Sbostic err2: (void)ftruncate(mbfd, curoff);
39259601Sbostic err1: (void)close(mbfd);
39369603Seric err0: unlockmbox();
39459601Sbostic return;
39559601Sbostic }
39659601Sbostic
39759601Sbostic /* Close and check -- NFS doesn't write until the close. */
39859601Sbostic if (close(mbfd)) {
39959601Sbostic e_to_sys(errno);
40059601Sbostic warn("%s: %s", path, strerror(errno));
40168005Seric unlockmbox();
40259601Sbostic return;
40359601Sbostic }
404579Sroot
40569603Seric if (setreuid(0, 0) < 0) {
40669603Seric e_to_sys(errno);
40769603Seric warn("setreuid(0, 0): %s", strerror(errno));
40869603Seric }
40969604Seric #ifdef DEBUG
41069603Seric printf("reset euid = %d\n", geteuid());
41169603Seric #endif
41268005Seric unlockmbox();
41359601Sbostic notifybiff(biffmsg);
414579Sroot }
415579Sroot
41668005Seric /*
41768005Seric * user.lock files are necessary for compatibility with other
41868005Seric * systems, e.g., when the mail spool file is NFS exported.
41968005Seric * Alas, mailbox locking is more than just a local matter.
42068005Seric * EPA 11/94.
42168005Seric */
42268005Seric
42368201Seric char lockname[MAXPATHLEN];
42468005Seric int locked = 0;
42568005Seric
lockmbox(path)42668005Seric lockmbox(path)
42768005Seric char *path;
42868005Seric {
42968005Seric int statfailed = 0;
43068005Seric
43168005Seric if (locked)
43268005Seric return;
43368005Seric sprintf(lockname, "%s.lock", path);
43468005Seric for (;; sleep(5)) {
43568005Seric int fd;
43668005Seric struct stat st;
43768005Seric time_t now;
43868005Seric
43968187Seric fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
44068187Seric if (fd >= 0) {
44168005Seric locked = 1;
44268187Seric close(fd);
44368005Seric return;
44468005Seric }
44568005Seric if (stat(lockname, &st) < 0) {
44668005Seric if (statfailed++ > 5)
44768005Seric return;
44868005Seric continue;
44968005Seric }
45068005Seric statfailed = 0;
45168005Seric time(&now);
45268005Seric if (now < st.st_ctime + 300)
45368005Seric continue;
45468005Seric unlink(lockname);
45568005Seric }
45668005Seric }
45768005Seric
unlockmbox()45868005Seric unlockmbox()
45968005Seric {
46068005Seric if (!locked)
46168005Seric return;
46268005Seric unlink(lockname);
46368005Seric locked = 0;
46468005Seric }
46568005Seric
46650092Sbostic void
notifybiff(msg)46716731Sralph notifybiff(msg)
46816731Sralph char *msg;
46916731Sralph {
47016731Sralph static struct sockaddr_in addr;
47116731Sralph static int f = -1;
47245976Sbostic struct hostent *hp;
47345976Sbostic struct servent *sp;
47445976Sbostic int len;
47516731Sralph
47645976Sbostic if (!addr.sin_family) {
47745976Sbostic /* Be silent if biff service not available. */
47845976Sbostic if (!(sp = getservbyname("biff", "udp")))
47945976Sbostic return;
48045976Sbostic if (!(hp = gethostbyname("localhost"))) {
48159601Sbostic warn("localhost: %s", strerror(errno));
48245976Sbostic return;
48316731Sralph }
48445976Sbostic addr.sin_family = hp->h_addrtype;
48567830Seric memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
48645976Sbostic addr.sin_port = sp->s_port;
48716731Sralph }
48845976Sbostic if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
48959601Sbostic warn("socket: %s", strerror(errno));
49045976Sbostic return;
49116731Sralph }
49245976Sbostic len = strlen(msg) + 1;
49346672Sbostic if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
49446672Sbostic != len)
49559601Sbostic warn("sendto biff: %s", strerror(errno));
49616731Sralph }
49716731Sralph
49850092Sbostic void
usage()49945976Sbostic usage()
500579Sroot {
50159601Sbostic eval = EX_USAGE;
50259601Sbostic err("usage: mail.local [-f from] user ...");
503579Sroot }
504579Sroot
50550092Sbostic #if __STDC__
50665558Sbostic void
err(const char * fmt,...)50759601Sbostic err(const char *fmt, ...)
50850092Sbostic #else
50964993Smckusick void
51059601Sbostic err(fmt, va_alist)
51159601Sbostic const char *fmt;
51259601Sbostic va_dcl
51350092Sbostic #endif
51459601Sbostic {
51559601Sbostic va_list ap;
51650092Sbostic
51759601Sbostic #if __STDC__
51859601Sbostic va_start(ap, fmt);
51959601Sbostic #else
52059601Sbostic va_start(ap);
52159601Sbostic #endif
52259601Sbostic vwarn(fmt, ap);
52359601Sbostic va_end(ap);
52459601Sbostic
52559601Sbostic exit(eval);
52659601Sbostic }
52759601Sbostic
52850092Sbostic void
52950092Sbostic #if __STDC__
warn(const char * fmt,...)53059601Sbostic warn(const char *fmt, ...)
53150092Sbostic #else
53259601Sbostic warn(fmt, va_alist)
53359601Sbostic const char *fmt;
53450092Sbostic va_dcl
53550092Sbostic #endif
536579Sroot {
53745976Sbostic va_list ap;
53859601Sbostic
53950092Sbostic #if __STDC__
54046554Sbostic va_start(ap, fmt);
54150092Sbostic #else
54250092Sbostic va_start(ap);
54350092Sbostic #endif
54459601Sbostic vwarn(fmt, ap);
54559601Sbostic va_end(ap);
54659601Sbostic }
54759601Sbostic
54859601Sbostic void
vwarn(fmt,ap)54959601Sbostic vwarn(fmt, ap)
55059601Sbostic const char *fmt;
55159601Sbostic _BSD_VA_LIST_ ap;
55259601Sbostic {
55358400Sbostic /*
55459601Sbostic * Log the message to stderr.
55559601Sbostic *
55659601Sbostic * Don't use LOG_PERROR as an openlog() flag to do this,
55759601Sbostic * it's not portable enough.
55858400Sbostic */
55959601Sbostic if (eval != EX_USAGE)
56059601Sbostic (void)fprintf(stderr, "mail.local: ");
56158396Sbostic (void)vfprintf(stderr, fmt, ap);
56258400Sbostic (void)fprintf(stderr, "\n");
56359601Sbostic
56469950Seric #if !defined(ultrix) && !defined(__osf__)
56559601Sbostic /* Log the message to syslog. */
56645976Sbostic vsyslog(LOG_ERR, fmt, ap);
56767830Seric #else
56867830Seric {
56967830Seric char fmtbuf[10240];
57067830Seric
57167830Seric (void) sprintf(fmtbuf, fmt, ap);
57267830Seric syslog(LOG_ERR, "%s", fmtbuf);
57367830Seric }
57467830Seric #endif
575579Sroot }
57659601Sbostic
57759601Sbostic /*
57859601Sbostic * e_to_sys --
57959601Sbostic * Guess which errno's are temporary. Gag me.
58059601Sbostic */
58159601Sbostic void
e_to_sys(num)58259601Sbostic e_to_sys(num)
58359601Sbostic int num;
58459601Sbostic {
58559601Sbostic /* Temporary failures override hard errors. */
58659601Sbostic if (eval == EX_TEMPFAIL)
58759601Sbostic return;
58859601Sbostic
58959601Sbostic switch(num) { /* Hopefully temporary errors. */
59059601Sbostic #ifdef EAGAIN
59159601Sbostic case EAGAIN: /* Resource temporarily unavailable */
59259601Sbostic #endif
59359601Sbostic #ifdef EDQUOT
59459601Sbostic case EDQUOT: /* Disc quota exceeded */
59559601Sbostic #endif
59659601Sbostic #ifdef EBUSY
59759601Sbostic case EBUSY: /* Device busy */
59859601Sbostic #endif
59959601Sbostic #ifdef EPROCLIM
60059601Sbostic case EPROCLIM: /* Too many processes */
60159601Sbostic #endif
60259601Sbostic #ifdef EUSERS
60359601Sbostic case EUSERS: /* Too many users */
60459601Sbostic #endif
60559601Sbostic #ifdef ECONNABORTED
60659601Sbostic case ECONNABORTED: /* Software caused connection abort */
60759601Sbostic #endif
60859601Sbostic #ifdef ECONNREFUSED
60959601Sbostic case ECONNREFUSED: /* Connection refused */
61059601Sbostic #endif
61159601Sbostic #ifdef ECONNRESET
61259601Sbostic case ECONNRESET: /* Connection reset by peer */
61359601Sbostic #endif
61459601Sbostic #ifdef EDEADLK
61559601Sbostic case EDEADLK: /* Resource deadlock avoided */
61659601Sbostic #endif
61759601Sbostic #ifdef EFBIG
61859601Sbostic case EFBIG: /* File too large */
61959601Sbostic #endif
62059601Sbostic #ifdef EHOSTDOWN
62159601Sbostic case EHOSTDOWN: /* Host is down */
62259601Sbostic #endif
62359601Sbostic #ifdef EHOSTUNREACH
62459601Sbostic case EHOSTUNREACH: /* No route to host */
62559601Sbostic #endif
62659601Sbostic #ifdef EMFILE
62759601Sbostic case EMFILE: /* Too many open files */
62859601Sbostic #endif
62959601Sbostic #ifdef ENETDOWN
63059601Sbostic case ENETDOWN: /* Network is down */
63159601Sbostic #endif
63259601Sbostic #ifdef ENETRESET
63359601Sbostic case ENETRESET: /* Network dropped connection on reset */
63459601Sbostic #endif
63559601Sbostic #ifdef ENETUNREACH
63659601Sbostic case ENETUNREACH: /* Network is unreachable */
63759601Sbostic #endif
63859601Sbostic #ifdef ENFILE
63959601Sbostic case ENFILE: /* Too many open files in system */
64059601Sbostic #endif
64159601Sbostic #ifdef ENOBUFS
64259601Sbostic case ENOBUFS: /* No buffer space available */
64359601Sbostic #endif
64459601Sbostic #ifdef ENOMEM
64559601Sbostic case ENOMEM: /* Cannot allocate memory */
64659601Sbostic #endif
64759601Sbostic #ifdef ENOSPC
64859601Sbostic case ENOSPC: /* No space left on device */
64959601Sbostic #endif
65059601Sbostic #ifdef EROFS
65159601Sbostic case EROFS: /* Read-only file system */
65259601Sbostic #endif
65359601Sbostic #ifdef ESTALE
65459601Sbostic case ESTALE: /* Stale NFS file handle */
65559601Sbostic #endif
65659601Sbostic #ifdef ETIMEDOUT
65759601Sbostic case ETIMEDOUT: /* Connection timed out */
65859601Sbostic #endif
65967830Seric #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
66059601Sbostic case EWOULDBLOCK: /* Operation would block. */
66159601Sbostic #endif
66259601Sbostic eval = EX_TEMPFAIL;
66359601Sbostic break;
66459601Sbostic default:
66559601Sbostic eval = EX_UNAVAILABLE;
66659601Sbostic break;
66759601Sbostic }
66859601Sbostic }
66967830Seric
67067830Seric #ifndef BSD4_4
67167830Seric
67269950Seric # ifndef __osf__
67367830Seric char *
strerror(eno)67467830Seric strerror(eno)
67567830Seric int eno;
67667830Seric {
67767830Seric extern int sys_nerr;
67867830Seric extern char *sys_errlist[];
67967830Seric static char ebuf[60];
68067830Seric
68167830Seric if (eno >= 0 && eno <= sys_nerr)
68267830Seric return sys_errlist[eno];
68367830Seric (void) sprintf(ebuf, "Error %d", eno);
68467830Seric return ebuf;
68567830Seric }
68669950Seric # endif
68767830Seric
68869950Seric # if __STDC__
snprintf(char * buf,int bufsiz,const char * fmt,...)68967830Seric snprintf(char *buf, int bufsiz, const char *fmt, ...)
69069950Seric # else
69167830Seric snprintf(buf, bufsiz, fmt, va_alist)
69267830Seric char *buf;
69367830Seric int bufsiz;
69467830Seric const char *fmt;
69567830Seric va_dcl
69669950Seric # endif
69767830Seric {
69867830Seric va_list ap;
69967830Seric
70069950Seric # if __STDC__
70167830Seric va_start(ap, fmt);
70269950Seric # else
70367830Seric va_start(ap);
70469950Seric # endif
70567830Seric vsprintf(buf, fmt, ap);
70667830Seric va_end(ap);
70767830Seric }
70867830Seric
70967830Seric #endif
71067830Seric
71167830Seric #ifdef ultrix
71267830Seric
71368189Seric /*
71468189Seric * Copyright (c) 1987, 1993
71568189Seric * The Regents of the University of California. All rights reserved.
71668189Seric *
71768189Seric * Redistribution and use in source and binary forms, with or without
71868189Seric * modification, are permitted provided that the following conditions
71968189Seric * are met:
72068189Seric * 1. Redistributions of source code must retain the above copyright
72168189Seric * notice, this list of conditions and the following disclaimer.
72268189Seric * 2. Redistributions in binary form must reproduce the above copyright
72368189Seric * notice, this list of conditions and the following disclaimer in the
72468189Seric * documentation and/or other materials provided with the distribution.
72568189Seric * 3. All advertising materials mentioning features or use of this software
72668189Seric * must display the following acknowledgement:
72768189Seric * This product includes software developed by the University of
72868189Seric * California, Berkeley and its contributors.
72968189Seric * 4. Neither the name of the University nor the names of its contributors
73068189Seric * may be used to endorse or promote products derived from this software
73168189Seric * without specific prior written permission.
73268189Seric *
73368189Seric * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
73468189Seric * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
73568189Seric * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73668189Seric * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
73768189Seric * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
73868189Seric * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
73968189Seric * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
74068189Seric * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
74168189Seric * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
74268189Seric * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
74368189Seric * SUCH DAMAGE.
74468189Seric */
74568189Seric
74668189Seric #if defined(LIBC_SCCS) && !defined(lint)
74768189Seric static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
74868189Seric #endif /* LIBC_SCCS and not lint */
74968189Seric
75068189Seric #include <sys/types.h>
75168189Seric #include <sys/stat.h>
75268189Seric #include <fcntl.h>
75368189Seric #include <errno.h>
75468189Seric #include <stdio.h>
75568189Seric #include <ctype.h>
75668189Seric
75768189Seric static int _gettemp();
75868189Seric
mkstemp(path)75968189Seric mkstemp(path)
76068189Seric char *path;
76167830Seric {
76267830Seric int fd;
76367830Seric
76468189Seric return (_gettemp(path, &fd) ? fd : -1);
76567830Seric }
76667830Seric
76768189Seric /*
76868189Seric char *
76968189Seric mktemp(path)
77068189Seric char *path;
77168189Seric {
77268189Seric return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
77368189Seric }
77468189Seric */
77568189Seric
77668189Seric static
_gettemp(path,doopen)77768189Seric _gettemp(path, doopen)
77868189Seric char *path;
77968189Seric register int *doopen;
78068189Seric {
78168189Seric extern int errno;
78268189Seric register char *start, *trv;
78368189Seric struct stat sbuf;
78468189Seric u_int pid;
78568189Seric
78668189Seric pid = getpid();
78768189Seric for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
78868189Seric while (*--trv == 'X') {
78968189Seric *trv = (pid % 10) + '0';
79068189Seric pid /= 10;
79168189Seric }
79268189Seric
79368189Seric /*
79468189Seric * check the target directory; if you have six X's and it
79568189Seric * doesn't exist this runs for a *very* long time.
79668189Seric */
79768189Seric for (start = trv + 1;; --trv) {
79868189Seric if (trv <= path)
79968189Seric break;
80068189Seric if (*trv == '/') {
80168189Seric *trv = '\0';
80268189Seric if (stat(path, &sbuf))
80368189Seric return(0);
80468189Seric if (!S_ISDIR(sbuf.st_mode)) {
80568189Seric errno = ENOTDIR;
80668189Seric return(0);
80768189Seric }
80868189Seric *trv = '/';
80968189Seric break;
81068189Seric }
81168189Seric }
81268189Seric
81368189Seric for (;;) {
81468189Seric if (doopen) {
81568189Seric if ((*doopen =
81668189Seric open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
81768189Seric return(1);
81868189Seric if (errno != EEXIST)
81968189Seric return(0);
82068189Seric }
82168189Seric else if (stat(path, &sbuf))
82268189Seric return(errno == ENOENT ? 1 : 0);
82368189Seric
82468189Seric /* tricky little algorithm for backward compatibility */
82568189Seric for (trv = start;;) {
82668189Seric if (!*trv)
82768189Seric return(0);
82868189Seric if (*trv == 'z')
82968189Seric *trv++ = 'a';
83068189Seric else {
83168189Seric if (isdigit(*trv))
83268189Seric *trv = 'a';
83368189Seric else
83468189Seric ++*trv;
83568189Seric break;
83668189Seric }
83768189Seric }
83868189Seric }
83968189Seric /*NOTREACHED*/
84068189Seric }
84168189Seric
84267830Seric #endif
843