#
/* 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);
}