# /* maild.c * * This is the network mail daemon. It repetitively scans the mail * daemon directory ("/maild"), looking for a request file (that is, * a file with extension ".req"). Then it attempts to mail the file * specified in the request file to each of the users specified in * the request file. When all users have been successfully reached, * the request file and the file being mailed are deleted. */ #include #include #include #include #include "rqfile.h" #include "extern.h" #include "tcode.h" #define SLEEPTIME 600 /* time to sleep in secs */ #define SIGCTLA 16 /* special interrupt signal */ main (argc, argv) int argc; char *argv[]; { int sleeptime = SLEEPTIME; /* time to sleep between scans */ int difd; /* directory file descriptor */ int rqfd; /* request file descriptor */ char *dirent; /* pointer to current entry */ char *mlfile; /* pointer to mail file name */ register struct req_rec *rqp; /* pointer to request record */ register int alldone = TRUE; /* success flag */ register int status; /* mail sending status */ if (argc > 1) sleeptime = atoi (argv[1]); if (argc > 2) { log = TRUE; setbuf (stdout, NULL); } setup (); /* initialize signals, etc */ getpwf (); /* get password entries */ #ifdef PMAIL getpmf (); /* and pmail users */ #endif if ((difd = open (maildir, 0)) < 0) faterr ("can't open mail directory\n"); for (;;) { alarm (sleeptime); pause (); /* wait for alarm or signal */ alarm (0); chkpwd (); /* update password entries if needed */ if ((dirent = get_rqfil (difd)) == 0) continue; if ((rqfd = open (dirent, 0)) < 0) { rerror ("can't open a request file", dirent, 0); continue; } rewnew = TRUE; if ((mlfile = get_mlfile (rqfd, dirent)) == 0) continue; alldone = TRUE; while ((rqp = get_dest (rqfd, dirent)) != 0) { if ((status = send_mail (mlfile, rqp)) == SKIP) break; else if (status == SAVE) alldone = FALSE; } close (rqfd); unlink (dirent); if (alldone) unlink (mlfile); else flush_rew (); } } setup () /* Initialize signals, set up lock file with pid, etc. */ { int lkfd; /* lock file descriptor */ int pid; /* process's id */ char cpid[10]; /* char pid for lock file */ int myuid; /* daemon's uid */ register char *mep; /* ptr to daemon's uname */ signal (SIGHUP, &finish); signal (SIGINT, 1); signal (SIGQUIT, 1); signal (SIGTERM, &finish); signal (SIGALRM, &nullproc); signal (SIGCTLA, &nullproc); close(0); /* don't need stdin */ chdir (maildir); if ((lkfd = creat (lockfile, 0444)) < 0) exit (-1); pid = getpid (); itos (cpid, pid, 10); write (lkfd, cpid, strlen (cpid) + 1); close (lkfd); /* Sort the host tables for faster access */ qsort (myhost, myhsiz, sizeof (myhost[0]), &lwccmp); qsort (tftp_htab, tfhsiz, sizeof (tftp_htab[0]), &lwccmp); qsort (smtp_htab, smhsiz, sizeof (smtp_htab[0]), &lwccmp); } char *get_rqfil (difd) /* Scan the mail directory looking for the presence of a request * file (ie a file with extension ".req"). If one is found, return * a pointer to its filename in the request buffer. * Note that this routine does block I/O and unblocks records for * itself to increase speed. Also, relies on the fact that directory * entries do not cross block boundaries. * * Returns: * pointer to filename of request file * * Arguments: */ int difd; /* directory file descriptor */ { register struct dir *ent; /* next directory entry */ register int dsize; /* size of directory record */ register int partscan; /* partial scan flag */ partscan = (dirrec != 0); /* in the middle of directory? */ for (;;) { /* If we reach the end of the current block, read in the next block */ if (dirrec == 0 || (dirend - dirrec) < sizeof (struct dir)) { dsize = read (difd, dirbuf, BIGBUF); dirrec = dirbuf; dirend = dirbuf + dsize; /* If read error, declare a fatal error */ if (dsize < 0) faterr ("directory file read error\n"); /* If end of file, seek to start of file. Then if this was a scan of the * full directory, return failure; otherwise, rescan starting at top. */ if (dsize == 0) { seek (difd, 0, 0); dirrec = dirend = 0; if (!partscan) return (0); partscan = FALSE; continue; } } /* Otherwise, scan directory entries looking for a ".req" file */ while (dirend - dirrec >= sizeof (struct dir)) { ent = (struct dir *)dirrec; dirrec += sizeof (struct dir); if ((ent->d_ino != 0) && (strcmp (ext (ent->d_name), rqext) == 0) ) return (ent->d_name); } } } char *get_mlfil (rqfd, dirent) /* Read in the first block of a request file and return the filename * of the file to be mailed (which occupies the first NAMSIZ bytes of * the file). Also set globals reqrec and reqend to point to the * first request record in the buffer and the end of the block in * the buffer, respectively. (Block I/O is performed to speed up * the program). * * Returns: * pointer to filename of file to be mailed (0 on error) * * Arguments: */ int rqfd; /* file descriptor of request file */ char *dirent; /* name of request file */ { register int rsize; /* size of next request file block */ rsize = read (rqfd, reqbuf, BIGBUF); if (rsize <= 0) { rerror ("can't read request file", dirent, rqfd); return (0); } reqrec = reqbuf + NAMSIZ; reqend = reqbuf + rsize; strcpy (mlname, reqbuf); return (mlname); } struct req_rec *get_dest (rqfd, dirent) /* Get the next destination from a request file. Returns a pointer * to the next request record, or 0 if end-of-file. * Note: does block I/O to increase program speed. * * Returns: * pointer to next request record * * Arguments: */ int rqfd; /* request file descriptor */ char *dirent; /* name of request file */ { register char *rptr; /* temporary pointer */ register int rsize; /* size of next block */ register struct req_rec *rent; /* pointer to next req. record */ /* If we reach the end of the current block, read in the next one */ if ((reqend - reqrec) < sizeof (struct req_rec)) { for (rptr = reqbuf; reqrec < reqend; rptr++, reqrec++) *rptr = *reqrec; rsize = read (rqfd, rptr, BIGBUF); reqrec = reqbuf; reqend = rptr + rsize; /* If read error, give up on this request file */ if (rsize < 0) rerror ("read error on request file", dirent, rqfd); /* If end-of-file, return failure */ if (rsize == 0) return (0); } /* Otherwise, return the next record of the file, if any (ie. if there is * less than one record remaining for some reason, ignore it) */ if ((reqend - reqrec) < sizeof (struct req_rec)) { reqrec = reqend; return (0); } else { rent = (struct req_rec *)reqrec; reqrec += sizeof (struct req_rec); return (rent); } } rewrite (mlfile, rqp) /* This routine rewrites a request file in the event that a mail * request fails. A new request file is generated, containing * just the entries for those requests which have failed. The * file is written using block I/O to increase execution speed. * * Arguments: */ char *mlfile; /* ptr. to name of mail file */ register struct req_rec *rqp; /* ptr. to rec. to be rewritten */ { char rewnam[NAMSIZ]; /* temp. for new file name */ register char *rewp; /* temp. ptr. */ int wsize; /* no. of bytes written */ /* If too many retries, give up on this transmission */ if (--(rqp->rq_retries) <= 0) { send_error (mlfile, rqp, TLTO); return (NOSAVE); } /* If no rewrite file open yet, open one */ if (rewnew) { rewnew = FALSE; gettmp (rewnam); if ((rwfd = creat (rewnam, 0600)) < 0) { werror ("can't open a request rewrite file\n", 0, 0); return (NOSAVE); } strcpy (rewbuf, mlfile); rewrec = &rewbuf[NAMSIZ]; } /* Copy record to buffer; if buffer becomes full, write it out */ for (rewp = (char *)rqp; rewp < rqp + 1; rewp++, rewrec++) { if (rewrec >= &rewbuf[BIGBUF]) { wsize = write (rwfd, rewbuf, BIGBUF); rewrec = rewbuf; if (wsize != BIGBUF) { werror ("write error on rewrite file\n", rewnam, rwfd); return (NOSAVE); } } *rewrec = *rewp; } return (SAVE); } flush_rew () /* This routine flushes out the contents of the rewrite buffer and * closes the rewrite file. */ { write (rwfd, rewbuf, rewrec - rewbuf); close (rwfd); rewrec = rewbuf; rewnew = TRUE; } faterr (str) /* This is the fatal error handler; it prints an error message, and calls * the clean-up and exit routine. * * Arguments: */ char *str; /* error message string */ { printf ("maild: %s", str); finish (0); } rerror (err, name, fd) /* This is the request file error handler. It prints an error message, * renames the request file to name.err (so that it won't ever be found * again), and returns. * * Arguments: */ char *err; /* error message string */ char *name; /* name of request file */ int fd; /* file descriptor of request file */ /* 0 => none */ { char tmp[NAMSIZ]; /* temp for rename string */ if (fd > 0) close (fd); printf ("maild: %s %s\n", err, name); strcpy (tmp, name); strcat (tmp, ".err"); link (name, tmp); unlink (name); reqrec = reqend = reqbuf; } werror (err, name, fd) /* This is the rewrite file error handler. It prints an error string, * closes the rewrite file (if open), removes the rewrite file (if any), * and returns. * * Arguments: */ char *err; /* error message string */ char *name; /* name of rewrite file */ /* 0 => none */ int fd; /* rewrite file descriptor */ /* 0 => none */ { if (fd > 0) close (fd); printf ("maild: %s %s\n", err, (name == 0 ? "" : name)); if (name != 0) unlink (name); rewrec = rewbuf; rewnew = TRUE; } finish (sig) /* This routine cleans up after the mail daemon and exits. It is * called either in a fatal error, or upon receiving an 'exit' * signal. It deletes the lock file from the daemon directory, * flushes out the contents of the rewrite buffer (if any), and * exits (relying on exit to close any open files, and the * system to take care of any children lying around). * * Arguments: */ int sig; /* received exit signal */ { if (!rewnew) flush_rew (); printf ("maild: exiting with signal %d\n", sig); unlink (lockfile); exit (0); } nullproc () /* Null procedure; just return. Used as a destination for signals * which we just want to wake up on. */ { signal (SIGALRM, &nullproc); signal (SIGCTLA, &nullproc); }