diff options
Diffstat (limited to 'contrib/sendmail/src/queue.c')
-rw-r--r-- | contrib/sendmail/src/queue.c | 1904 |
1 files changed, 1425 insertions, 479 deletions
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c index b02fc08..209d5b7 100644 --- a/contrib/sendmail/src/queue.c +++ b/contrib/sendmail/src/queue.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. @@ -10,20 +11,28 @@ * */ -# include "sendmail.h" + +#include <sendmail.h> #ifndef lint -#if QUEUE -static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (with queueing)"; -#else -static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (without queueing)"; -#endif -#endif /* not lint */ +# if QUEUE +static char id[] = "@(#)$Id: queue.c,v 8.343.4.11 2000/07/14 05:55:51 gshapiro Exp $ (with queueing)"; +# else /* QUEUE */ +static char id[] = "@(#)$Id: queue.c,v 8.343.4.11 2000/07/14 05:55:51 gshapiro Exp $ (without queueing)"; +# endif /* QUEUE */ +#endif /* ! lint */ -# include <errno.h> # include <dirent.h> -# if QUEUE +#if QUEUE + +# if _FFR_QUEUEDELAY +# define QF_VERSION 5 /* version number of this queue format */ +static time_t queuedelay __P((ENVELOPE *)); +# else /* _FFR_QUEUEDELAY */ +# define QF_VERSION 4 /* version number of this queue format */ +# define queuedelay(e) MinQueueAge +# endif /* _FFR_QUEUEDELAY */ /* ** Work queue. @@ -42,11 +51,23 @@ struct work typedef struct work WORK; -WORK *WorkQ; /* queue of things to be done */ +static WORK *WorkQ; /* queue of things to be done */ + +static void grow_wlist __P((int)); +static int orderq __P((int, bool)); +static void printctladdr __P((ADDRESS *, FILE *)); +static int print_single_queue __P((int)); +static bool readqf __P((ENVELOPE *)); +static void runqueueevent __P((void)); +static int run_single_queue __P((int, bool, bool)); +static char *strrev __P((char *)); +static ADDRESS *setctluser __P((char *, int)); +static int workcmpf0(); +static int workcmpf1(); +static int workcmpf2(); +static int workcmpf3(); +static int workcmpf4(); -#define QF_VERSION 2 /* version number of this queue format */ - -extern int orderq __P((bool)); /* ** QUEUEUP -- queue a message up for future transmission. ** @@ -62,6 +83,8 @@ extern int orderq __P((bool)); ** The queue file is left locked. */ +# define TEMPQF_LETTER 'T' + void queueup(e, announce) register ENVELOPE *e; @@ -71,15 +94,14 @@ queueup(e, announce) register FILE *tfp; register HDR *h; register ADDRESS *q; - int fd; + int tfd = -1; int i; bool newid; register char *p; MAILER nullmailer; MCI mcibuf; - char tf[MAXQFNAME]; + char tf[MAXPATHLEN]; char buf[MAXLINE]; - extern void printctladdr __P((ADDRESS *, FILE *)); /* ** Create control file. @@ -88,7 +110,7 @@ queueup(e, announce) newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); /* if newid, queuename will create a locked qf file in e->lockfp */ - strcpy(tf, queuename(e, 't')); + (void) strlcpy(tf, queuename(e, 't'), sizeof tf); tfp = e->e_lockfp; if (tfp == NULL) newid = FALSE; @@ -96,68 +118,92 @@ queueup(e, announce) /* if newid, just write the qf file directly (instead of tf file) */ if (!newid) { + int flags; + + flags = O_CREAT|O_WRONLY|O_EXCL; + /* get a locked tf file */ for (i = 0; i < 128; i++) { - fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); - if (fd < 0) + if (tfd < 0) { - if (errno != EEXIST) - break; - if (LogLevel > 0 && (i % 32) == 0) - sm_syslog(LOG_ALERT, e->e_id, - "queueup: cannot create %s, uid=%d: %s", - tf, geteuid(), errstring(errno)); +#if _FFR_QUEUE_FILE_MODE + MODE_T oldumask; + + if (bitset(S_IWGRP, QueueFileMode)) + oldumask = umask(002); + tfd = open(tf, flags, QueueFileMode); + if (bitset(S_IWGRP, QueueFileMode)) + (void) umask(oldumask); +#else /* _FFR_QUEUE_FILE_MODE */ + tfd = open(tf, flags, FileMode); +#endif /* _FFR_QUEUE_FILE_MODE */ + + if (tfd < 0) + { + if (errno != EEXIST) + break; + if (LogLevel > 0 && (i % 32) == 0) + sm_syslog(LOG_ALERT, e->e_id, + "queueup: cannot create %s, uid=%d: %s", + tf, geteuid(), errstring(errno)); + } } - else + if (tfd >= 0) { - if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) + if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) break; else if (LogLevel > 0 && (i % 32) == 0) sm_syslog(LOG_ALERT, e->e_id, - "queueup: cannot lock %s: %s", - tf, errstring(errno)); - close(fd); + "queueup: cannot lock %s: %s", + tf, errstring(errno)); + if ((i % 32) == 31) + { + (void) close(tfd); + tfd = -1; + } } if ((i % 32) == 31) { /* save the old temp file away */ - (void) rename(tf, queuename(e, 'T')); + (void) rename(tf, queuename(e, TEMPQF_LETTER)); } else - sleep(i % 32); + (void) sleep(i % 32); } - if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) + if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL) { + int save_errno = errno; + printopenfds(TRUE); + errno = save_errno; syserr("!queueup: cannot create queue temp file %s, uid=%d", tf, geteuid()); } } if (tTd(40, 1)) - printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id, + dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n", + qid_printqueue(e->e_queuedir), e->e_id, newid ? " (new id)" : ""); if (tTd(40, 3)) { - extern void printenvflags __P((ENVELOPE *)); - - printf(" e_flags="); + dprintf(" e_flags="); printenvflags(e); } if (tTd(40, 32)) { - printf(" sendq="); + dprintf(" sendq="); printaddr(e->e_sendqueue, TRUE); } if (tTd(40, 9)) { - printf(" tfp="); + dprintf(" tfp="); dumpfd(fileno(tfp), TRUE, FALSE); - printf(" lockfp="); + dprintf(" lockfp="); if (e->e_lockfp == NULL) - printf("NULL\n"); + dprintf("NULL\n"); else dumpfd(fileno(e->e_lockfp), TRUE, FALSE); } @@ -166,18 +212,41 @@ queueup(e, announce) ** If there is no data file yet, create one. */ - if (!bitset(EF_HAS_DF, e->e_flags)) + if (bitset(EF_HAS_DF, e->e_flags)) { + if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0) + syserr("!queueup: cannot commit data file %s, uid=%d", + queuename(e, 'd'), geteuid()); + } + else + { + int dfd; register FILE *dfp = NULL; - char dfname[MAXQFNAME]; + char dfname[MAXPATHLEN]; struct stat stbuf; - strcpy(dfname, queuename(e, 'd')); - fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); - if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) + if (e->e_dfp != NULL && bftest(e->e_dfp)) + syserr("committing over bf file"); + + (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); +#if _FFR_QUEUE_FILE_MODE + { + MODE_T oldumask; + + if (bitset(S_IWGRP, QueueFileMode)) + oldumask = umask(002); + dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, + QueueFileMode); + if (bitset(S_IWGRP, QueueFileMode)) + (void) umask(oldumask); + } +#else /* _FFR_QUEUE_FILE_MODE */ + dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); +#endif /* _FFR_QUEUE_FILE_MODE */ + if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL) syserr("!queueup: cannot create data temp file %s, uid=%d", dfname, geteuid()); - if (fstat(fd, &stbuf) < 0) + if (fstat(dfd, &stbuf) < 0) e->e_dfino = -1; else { @@ -185,11 +254,13 @@ queueup(e, announce) e->e_dfino = stbuf.st_ino; } e->e_flags |= EF_HAS_DF; - bzero(&mcibuf, sizeof mcibuf); + memset(&mcibuf, '\0', sizeof mcibuf); mcibuf.mci_out = dfp; mcibuf.mci_mailer = FileMailer; (*e->e_putbody)(&mcibuf, e, NULL); - (void) xfclose(dfp, "queueup dfp", e->e_id); + if (fclose(dfp) < 0) + syserr("!queueup: cannot save data temp file %s, uid=%d", + dfname, geteuid()); e->e_putbody = putbody; } @@ -206,7 +277,17 @@ queueup(e, announce) fprintf(tfp, "T%ld\n", (long) e->e_ctime); /* output last delivery time */ +# if _FFR_QUEUEDELAY + fprintf(tfp, "K%ld\n", (long) e->e_dtime); + fprintf(tfp, "G%d\n", e->e_queuealg); + fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay); + if (tTd(40, 64)) + sm_syslog(LOG_INFO, e->e_id, + "queue alg: %d delay %ld next: %ld (now: %ld)\n", + e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime()); +# else /* _FFR_QUEUEDELAY */ fprintf(tfp, "K%ld\n", (long) e->e_dtime); +# endif /* _FFR_QUEUEDELAY */ /* output number of delivery attempts */ fprintf(tfp, "N%d\n", e->e_ntries); @@ -218,13 +299,16 @@ queueup(e, announce) /* XXX should probably include device major/minor too */ if (e->e_dfino != -1) { + /*CONSTCOND*/ if (sizeof e->e_dfino > sizeof(long)) - fprintf(tfp, "I%d/%d/%s\n", - major(e->e_dfdev), minor(e->e_dfdev), + fprintf(tfp, "I%ld/%ld/%s\n", + (long) major(e->e_dfdev), + (long) minor(e->e_dfdev), quad_to_string(e->e_dfino)); else - fprintf(tfp, "I%d/%d/%lu\n", - major(e->e_dfdev), minor(e->e_dfdev), + fprintf(tfp, "I%ld/%ld/%lu\n", + (long) major(e->e_dfdev), + (long) minor(e->e_dfdev), (unsigned long) e->e_dfino); } @@ -232,10 +316,10 @@ queueup(e, announce) if (e->e_bodytype != NULL) fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); -#if _FFR_SAVE_CHARSET +# if _FFR_SAVE_CHARSET if (e->e_charset != NULL) fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); -#endif +# endif /* _FFR_SAVE_CHARSET */ /* message from envelope, if it exists */ if (e->e_message != NULL) @@ -259,13 +343,8 @@ queueup(e, announce) if (buf[0] != '\0') fprintf(tfp, "F%s\n", buf); - /* $r and $s and $_ macro values */ - if ((p = macvalue('r', e)) != NULL) - fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); - if ((p = macvalue('s', e)) != NULL) - fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); - if ((p = macvalue('_', e)) != NULL) - fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); + /* save $={persistentMacros} macro values */ + queueup_macros(macid("{persistentMacros}", NULL), tfp, e); /* output name of sender */ if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) @@ -278,49 +357,47 @@ queueup(e, announce) if (e->e_envid != NULL) fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); + /* output AUTH= parameter */ + if (e->e_auth_param != NULL) + fprintf(tfp, "A%s\n", denlstring(e->e_auth_param, + TRUE, FALSE)); + /* output list of recipient addresses */ printctladdr(NULL, NULL); for (q = e->e_sendqueue; q != NULL; q = q->q_next) { - if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)) - { -#if XDEBUG - if (bitset(QQUEUEUP, q->q_flags)) - sm_syslog(LOG_DEBUG, e->e_id, - "dropenvelope: q_flags = %x, paddr = %s", - q->q_flags, q->q_paddr); -#endif + if (!QS_IS_UNDELIVERED(q->q_state)) continue; - } + printctladdr(q, tfp); if (q->q_orcpt != NULL) fprintf(tfp, "Q%s\n", denlstring(q->q_orcpt, TRUE, FALSE)); - putc('R', tfp); + (void) putc('R', tfp); if (bitset(QPRIMARY, q->q_flags)) - putc('P', tfp); + (void) putc('P', tfp); if (bitset(QHASNOTIFY, q->q_flags)) - putc('N', tfp); + (void) putc('N', tfp); if (bitset(QPINGONSUCCESS, q->q_flags)) - putc('S', tfp); + (void) putc('S', tfp); if (bitset(QPINGONFAILURE, q->q_flags)) - putc('F', tfp); + (void) putc('F', tfp); if (bitset(QPINGONDELAY, q->q_flags)) - putc('D', tfp); - putc(':', tfp); - fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); + (void) putc('D', tfp); + (void) putc(':', tfp); + (void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); if (announce) { e->e_to = q->q_paddr; message("queued"); if (LogLevel > 8) - logdelivery(q->q_mailer, NULL, "queued", - NULL, (time_t) 0, e); + logdelivery(q->q_mailer, NULL, q->q_status, + "queued", NULL, (time_t) 0, e); e->e_to = NULL; } if (tTd(40, 1)) { - printf("queueing "); + dprintf("queueing "); printaddr(q, FALSE); } } @@ -335,24 +412,23 @@ queueup(e, announce) ** no effect on the addresses as they are output. */ - bzero((char *) &nullmailer, sizeof nullmailer); + memset((char *) &nullmailer, '\0', sizeof nullmailer); nullmailer.m_re_rwset = nullmailer.m_rh_rwset = nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; nullmailer.m_eol = "\n"; - bzero(&mcibuf, sizeof mcibuf); + memset(&mcibuf, '\0', sizeof mcibuf); mcibuf.mci_mailer = &nullmailer; mcibuf.mci_out = tfp; define('g', "\201f", e); for (h = e->e_header; h != NULL; h = h->h_link) { - extern bool bitzerop __P((BITMAP)); - if (h->h_value == NULL) continue; /* don't output resent headers on non-resent messages */ - if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) + if (bitset(H_RESENT, h->h_flags) && + !bitset(EF_RESENT, e->e_flags)) continue; /* expand macros; if null, don't output header at all */ @@ -364,28 +440,39 @@ queueup(e, announce) } /* output this header */ - fprintf(tfp, "H"); + fprintf(tfp, "H?"); - /* if conditional, output the set of conditions */ - if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) + /* output conditional macro if present */ + if (h->h_macro != '\0') + { + if (bitset(0200, h->h_macro)) + fprintf(tfp, "${%s}", + macname(h->h_macro & 0377)); + else + fprintf(tfp, "$%c", h->h_macro); + } + else if (!bitzerop(h->h_mflags) && + bitset(H_CHECK|H_ACHECK, h->h_flags)) { int j; - (void) putc('?', tfp); + /* if conditional, output the set of conditions */ for (j = '\0'; j <= '\177'; j++) if (bitnset(j, h->h_mflags)) (void) putc(j, tfp); - (void) putc('?', tfp); } + (void) putc('?', tfp); /* output the header: expand macros, convert addresses */ - if (bitset(H_DEFAULT, h->h_flags)) + if (bitset(H_DEFAULT, h->h_flags) && + !bitset(H_BINDLATE, h->h_flags)) { fprintf(tfp, "%s: %s\n", h->h_field, denlstring(buf, FALSE, TRUE)); } - else if (bitset(H_FROM|H_RCPT, h->h_flags)) + else if (bitset(H_FROM|H_RCPT, h->h_flags) && + !bitset(H_BINDLATE, h->h_flags)) { bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); FILE *savetrace = TrafficLogFile; @@ -434,9 +521,20 @@ queueup(e, announce) syserr("cannot rename(%s, %s), uid=%d", tf, qf, geteuid()); + /* + ** fsync() after renaming to make sure + ** metadata is written to disk on + ** filesystems in which renames are + ** not guaranteed such as softupdates. + */ + + if (tfd >= 0 && SuperSafe && fsync(tfd) < 0) + syserr("!queueup: cannot fsync queue temp file %s", + tf); + /* close and unlock old (locked) qf */ if (e->e_lockfp != NULL) - (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); + (void) fclose(e->e_lockfp); e->e_lockfp = tfp; } else @@ -449,16 +547,16 @@ queueup(e, announce) sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); if (tTd(40, 1)) - printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); + dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); return; } -void +static void printctladdr(a, tfp) register ADDRESS *a; FILE *tfp; { - char *uname; + char *user; register ADDRESS *q; uid_t uid; gid_t gid; @@ -479,13 +577,13 @@ printctladdr(a, tfp) q = getctladdr(a); if (q == NULL) { - uname = NULL; + user = NULL; uid = 0; gid = 0; } else { - uname = q->q_ruser != NULL ? q->q_ruser : q->q_user; + user = q->q_ruser != NULL ? q->q_ruser : q->q_user; uid = q->q_uid; gid = q->q_gid; } @@ -498,11 +596,11 @@ printctladdr(a, tfp) lastuid = uid; lastctladdr = a; - if (uid == 0 || uname == NULL || uname[0] == '\0') + if (uid == 0 || user == NULL || user[0] == '\0') fprintf(tfp, "C"); else fprintf(tfp, "C%s:%ld:%ld", - denlstring(uname, TRUE, FALSE), (long) uid, (long) gid); + denlstring(user, TRUE, FALSE), (long) uid, (long) gid); fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); } /* @@ -515,6 +613,7 @@ printctladdr(a, tfp) ** forkflag -- TRUE if the queue scanning should be done in ** a child process. We double-fork so it is not our ** child and we don't have to clean up after it. +** FALSE can be ignored if we have multiple queues. ** verbose -- if TRUE, print out status information. ** ** Returns: @@ -524,21 +623,95 @@ printctladdr(a, tfp) ** runs things in the mail queue. */ -ENVELOPE QueueEnvelope; /* the queue run envelope */ -extern int get_num_procs_online __P((void)); +static ENVELOPE QueueEnvelope; /* the queue run envelope */ +int NumQueues = 0; /* number of queues */ +static time_t LastQueueTime = 0; /* last time a queue ID assigned */ +static pid_t LastQueuePid = -1; /* last PID which had a queue ID */ + +struct qpaths_s +{ + char *qp_name; /* name of queue dir */ + short qp_subdirs; /* use subdirs? */ +}; + +typedef struct qpaths_s QPATHS; + +/* values for qp_supdirs */ +#define QP_NOSUB 0x0000 /* No subdirectories */ +#define QP_SUBDF 0x0001 /* "df" subdirectory */ +#define QP_SUBQF 0x0002 /* "qf" subdirectory */ +#define QP_SUBXF 0x0004 /* "xf" subdirectory */ + +static QPATHS *QPaths = NULL; /* list of queue directories */ bool runqueue(forkflag, verbose) bool forkflag; bool verbose; { + int i; + bool ret = TRUE; + static int curnum = 0; + + if (!forkflag && NumQueues > 1 && !verbose) + forkflag = TRUE; + + for (i = 0; i < NumQueues; i++) + { + /* + ** Pick up where we left off, in case we + ** used up all the children last time + ** without finishing. + */ + + ret = run_single_queue(curnum, forkflag, verbose); + + /* + ** Failure means a message was printed for ETRN + ** and subsequent queues are likely to fail as well. + */ + + if (!ret) + break; + + if (++curnum >= NumQueues) + curnum = 0; + } + if (QueueIntvl != 0) + (void) setevent(QueueIntvl, runqueueevent, 0); + return ret; +} +/* +** RUN_SINGLE_QUEUE -- run the jobs in a single queue. +** +** Gets the stuff out of the queue in some presumably logical +** order and processes them. +** +** Parameters: +** queuedir -- queue to process +** forkflag -- TRUE if the queue scanning should be done in +** a child process. We double-fork so it is not our +** child and we don't have to clean up after it. +** verbose -- if TRUE, print out status information. +** +** Returns: +** TRUE if the queue run successfully began. +** +** Side Effects: +** runs things in the mail queue. +*/ + +static bool +run_single_queue(queuedir, forkflag, verbose) + int queuedir; + bool forkflag; + bool verbose; +{ register ENVELOPE *e; int njobs; int sequenceno = 0; time_t current_la_time; extern ENVELOPE BlankEnvelope; - extern void clrdaemon __P((void)); - extern void runqueueevent __P((void)); DoQueueRun = FALSE; @@ -547,7 +720,7 @@ runqueue(forkflag, verbose) ** the queue. */ - CurrentLA = getla(); /* get load average */ + CurrentLA = sm_getla(NULL); /* get load average */ current_la_time = curtime(); if (shouldqueue(WkRecipFact, current_la_time)) @@ -558,10 +731,8 @@ runqueue(forkflag, verbose) message("458 %s\n", msg); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); - if (forkflag && QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); + "runqueue: %s", + msg); return FALSE; } @@ -572,7 +743,14 @@ runqueue(forkflag, verbose) if (forkflag && QueueIntvl != 0 && MaxChildren > 0 && CurChildren >= MaxChildren) { - (void) setevent(QueueIntvl, runqueueevent, 0); + char *msg = "Skipping queue run -- too many children"; + + if (verbose) + message("458 %s (%d)\n", msg, CurChildren); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "runqueue: %s (%d)", + msg, CurChildren); return FALSE; } @@ -583,10 +761,8 @@ runqueue(forkflag, verbose) if (forkflag) { pid_t pid; - extern SIGFUNC_DECL intsig __P((int)); - extern SIGFUNC_DECL reapchild __P((int)); - blocksignal(SIGCHLD); + (void) blocksignal(SIGCHLD); (void) setsignal(SIGCHLD, reapchild); pid = dofork(); @@ -599,10 +775,8 @@ runqueue(forkflag, verbose) message("458 %s: %s\n", msg, err); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, - "runqueue: %s: %s", - msg, err); - if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); + "runqueue: %s: %s", + msg, err); (void) releasesignal(SIGCHLD); return FALSE; } @@ -610,30 +784,31 @@ runqueue(forkflag, verbose) { /* parent -- pick up intermediate zombie */ (void) blocksignal(SIGALRM); - proc_list_add(pid, "Queue runner"); + proc_list_add(pid, "Queue runner", PROC_QUEUE); (void) releasesignal(SIGALRM); - releasesignal(SIGCHLD); - if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); + (void) releasesignal(SIGCHLD); return TRUE; } - /* child -- double fork and clean up signals */ + /* child -- clean up signals */ clrcontrol(); proc_list_clear(); /* Add parent process as first child item */ - proc_list_add(getpid(), "Queue runner child process"); - releasesignal(SIGCHLD); + proc_list_add(getpid(), "Queue runner child process", + PROC_QUEUE_CHILD); + (void) releasesignal(SIGCHLD); (void) setsignal(SIGCHLD, SIG_DFL); (void) setsignal(SIGHUP, intsig); + } - sm_setproctitle(TRUE, "running queue: %s", QueueDir); + sm_setproctitle(TRUE, CurEnv, "running queue: %s", + qid_printqueue(queuedir)); - if (LogLevel > 69) + if (LogLevel > 69 || tTd(63, 99)) sm_syslog(LOG_DEBUG, NOQID, - "runqueue %s, pid=%d, forkflag=%d", - QueueDir, getpid(), forkflag); + "runqueue %s, pid=%d, forkflag=%d", + qid_printqueue(queuedir), getpid(), forkflag); /* ** Release any resources used by the daemon code. @@ -666,12 +841,6 @@ runqueue(forkflag, verbose) } /* - ** Make sure the alias database is open. - */ - - initmaps(FALSE, e); - - /* ** If we are running part of the queue, always ignore stored ** host status. */ @@ -691,7 +860,8 @@ runqueue(forkflag, verbose) */ /* order the existing work requests */ - njobs = orderq(FALSE); + njobs = orderq(queuedir, FALSE); + /* process them once at a time */ while (WorkQ != NULL) @@ -709,7 +879,7 @@ runqueue(forkflag, verbose) if (current_la_time < curtime() - 30) { - CurrentLA = getla(); + CurrentLA = sm_getla(e); current_la_time = curtime(); } if (shouldqueue(WkRecipFact, current_la_time)) @@ -720,8 +890,8 @@ runqueue(forkflag, verbose) message("%s", msg); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); + "runqueue: %s", + msg); break; } sequenceno++; @@ -729,26 +899,30 @@ runqueue(forkflag, verbose) { if (Verbose) message(""); - if (QueueSortOrder == QS_BYPRIORITY) + if (QueueSortOrder == QSO_BYPRIORITY) { if (Verbose) - message("Skipping %s (sequence %d of %d) and flushing rest of queue", + message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", + qid_printqueue(queuedir), w->w_name + 2, sequenceno, njobs); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, - "runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)", - w->w_name + 2, - w->w_pri, - CurrentLA, - sequenceno, - njobs); + "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", + qid_printqueue(queuedir), + w->w_name + 2, + w->w_pri, + CurrentLA, + sequenceno, + njobs); break; } else if (Verbose) - message("Skipping %s (sequence %d of %d)", - w->w_name + 2, sequenceno, njobs); + message("Skipping %s/%s (sequence %d of %d)", + qid_printqueue(queuedir), + w->w_name + 2, + sequenceno, njobs); } else { @@ -757,10 +931,19 @@ runqueue(forkflag, verbose) if (Verbose) { message(""); - message("Running %s (sequence %d of %d)", - w->w_name + 2, sequenceno, njobs); + message("Running %s/%s (sequence %d of %d)", + qid_printqueue(queuedir), + w->w_name + 2, + sequenceno, njobs); } - pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); + if (tTd(63, 100)) + sm_syslog(LOG_DEBUG, NOQID, + "runqueue %s dowork(%s)", + qid_printqueue(queuedir), + w->w_name + 2); + + pid = dowork(queuedir, w->w_name + 2, + ForkQueueRuns, FALSE, e); errno = 0; if (pid != 0) (void) waitfor(pid); @@ -773,17 +956,17 @@ runqueue(forkflag, verbose) /* exit without the usual cleanup */ e->e_id = NULL; - finis(TRUE, ExitStat); - /*NOTREACHED*/ + if (forkflag) + finis(TRUE, ExitStat); + /* NOTREACHED */ return TRUE; } - /* ** RUNQUEUEEVENT -- stub for use in setevent */ -void +static void runqueueevent() { DoQueueRun = TRUE; @@ -792,6 +975,7 @@ runqueueevent() ** ORDERQ -- order the work queue. ** ** Parameters: +** queuedir -- the index of the queue directory. ** doall -- if set, include everything in the queue (even ** the jobs that cannot be run because the load ** average is too high). Otherwise, exclude those @@ -813,8 +997,9 @@ runqueueevent() static WORK *WorkList = NULL; static int WorkListSize = 0; -int -orderq(doall) +static int +orderq(queuedir, doall) + int queuedir; bool doall; { register struct dirent *d; @@ -825,32 +1010,41 @@ orderq(doall) int wn = -1; int wc; QUEUE_CHAR *check; - + char qd[MAXPATHLEN]; + char qf[MAXPATHLEN]; + + if (queuedir == NOQDIR) + (void) strlcpy(qd, ".", sizeof qd); + else + (void) snprintf(qd, sizeof qd, "%s%s", + QPaths[queuedir].qp_name, + (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); + if (tTd(41, 1)) { - printf("orderq:\n"); + dprintf("orderq:\n"); check = QueueLimitId; while (check != NULL) { - printf("\tQueueLimitId = %s\n", - check->queue_match); + dprintf("\tQueueLimitId = %s\n", + check->queue_match); check = check->queue_next; } check = QueueLimitSender; while (check != NULL) { - printf("\tQueueLimitSender = %s\n", - check->queue_match); + dprintf("\tQueueLimitSender = %s\n", + check->queue_match); check = check->queue_next; } check = QueueLimitRecipient; while (check != NULL) { - printf("\tQueueLimitRecipient = %s\n", - check->queue_match); + dprintf("\tQueueLimitRecipient = %s\n", + check->queue_match); check = check->queue_next; } } @@ -869,11 +1063,11 @@ orderq(doall) } /* open the queue directory */ - f = opendir("."); + f = opendir(qd); if (f == NULL) { - syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); - return (0); + syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir)); + return 0; } /* @@ -885,24 +1079,24 @@ orderq(doall) FILE *cf; int qfver = 0; char lbuf[MAXNAME + 1]; - extern bool strcontainedin __P((char *, char *)); + struct stat sbuf; if (tTd(41, 50)) - printf("orderq: checking %s\n", d->d_name); + dprintf("orderq: checking %s\n", d->d_name); /* is this an interesting entry? */ if (d->d_name[0] != 'q' || d->d_name[1] != 'f') continue; - if (strlen(d->d_name) > MAXQFNAME) + if (strlen(d->d_name) >= MAXQFNAME) { if (Verbose) printf("orderq: %s too long, %d max characters\n", d->d_name, MAXQFNAME); if (LogLevel > 0) sm_syslog(LOG_ALERT, NOQID, - "orderq: %s too long, %d max characters", - d->d_name, MAXQFNAME); + "orderq: %s too long, %d max characters", + d->d_name, MAXQFNAME); continue; } @@ -917,69 +1111,67 @@ orderq(doall) if (QueueLimitId != NULL && check == NULL) continue; -#ifdef PICKY_QF_NAME_CHECK - /* - ** Check queue name for plausibility. This handles - ** both old and new type ids. - */ - - p = d->d_name + 2; - if (isupper(p[0]) && isupper(p[2])) - p += 3; - else if (isupper(p[1])) - p += 2; - else - p = d->d_name; - for (i = 0; isdigit(*p); p++) - i++; - if (i < 5 || *p != '\0') - { - if (Verbose) - printf("orderq: bogus qf name %s\n", d->d_name); - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "orderq: bogus qf name %s", - d->d_name); - if (strlen(d->d_name) > (SIZE_T) MAXNAME) - d->d_name[MAXNAME] = '\0'; - strcpy(lbuf, d->d_name); - lbuf[0] = 'Q'; - (void) rename(d->d_name, lbuf); - continue; - } -#endif - - /* open control file (if not too many files) */ + /* grow work list if necessary */ if (++wn >= MaxQueueRun && MaxQueueRun > 0) { if (wn == MaxQueueRun && LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "WorkList for %s maxed out at %d", - QueueDir, MaxQueueRun); + sm_syslog(LOG_WARNING, NOQID, + "WorkList for %s maxed out at %d", + qid_printqueue(queuedir), + MaxQueueRun); continue; } if (wn >= WorkListSize) { - extern void grow_wlist __P((void)); - - grow_wlist(); + grow_wlist(queuedir); if (wn >= WorkListSize) continue; } + w = &WorkList[wn]; - cf = fopen(d->d_name, "r"); + (void) snprintf(qf, sizeof qf, "%s/%s", qd, d->d_name); + if (stat(qf, &sbuf) < 0) + { + if (errno != ENOENT) + sm_syslog(LOG_INFO, NOQID, + "orderq: can't stat %s/%s", + qid_printqueue(queuedir), d->d_name); + wn--; + continue; + } + if (!bitset(S_IFREG, sbuf.st_mode)) + { + /* Yikes! Skip it or we will hang on open! */ + syserr("orderq: %s/%s is not a regular file", + qid_printqueue(queuedir), d->d_name); + wn--; + continue; + } + + /* avoid work if possible */ + if (QueueSortOrder == QSO_BYFILENAME) + { + w->w_name = newstr(d->d_name); + w->w_host = NULL; + w->w_lock = w->w_tooyoung = FALSE; + w->w_pri = 0; + w->w_ctime = 0; + continue; + } + + /* open control file */ + cf = fopen(qf, "r"); if (cf == NULL) { /* this may be some random person sending hir msgs */ /* syserr("orderq: cannot open %s", cbuf); */ if (tTd(41, 2)) - printf("orderq: cannot open %s: %s\n", + dprintf("orderq: cannot open %s: %s\n", d->d_name, errstring(errno)); errno = 0; wn--; continue; } - w = &WorkList[wn]; w->w_name = newstr(d->d_name); w->w_host = NULL; w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); @@ -993,13 +1185,12 @@ orderq(doall) i = NEED_P | NEED_T; if (QueueLimitSender != NULL) i |= NEED_S; - if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) + if (QueueLimitRecipient != NULL) i |= NEED_R; while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) { int c; time_t age; - extern bool strcontainedin __P((char *, char *)); p = strchr(lbuf, '\n'); if (p != NULL) @@ -1030,7 +1221,10 @@ orderq(doall) case 'R': if (w->w_host == NULL && (p = strrchr(&lbuf[1], '@')) != NULL) - w->w_host = newstr(&p[1]); + { + w->w_host = strrev(&p[1]); + makelower(w->w_host); + } if (QueueLimitRecipient == NULL) { i &= ~NEED_R; @@ -1058,17 +1252,17 @@ orderq(doall) break; case 'S': - check = QueueLimitSender; - while (check != NULL) - { - if (strcontainedin(check->queue_match, - &lbuf[1])) - break; - else - check = check->queue_next; - } - if (check != NULL) - i &= ~NEED_S; + check = QueueLimitSender; + while (check != NULL) + { + if (strcontainedin(check->queue_match, + &lbuf[1])) + break; + else + check = check->queue_next; + } + if (check != NULL) + i &= ~NEED_S; break; case 'K': @@ -1082,6 +1276,17 @@ orderq(doall) if (atol(&lbuf[1]) == 0) w->w_tooyoung = FALSE; break; + +# if _FFR_QUEUEDELAY +/* + case 'G': + queuealg = atoi(lbuf[1]); + break; + case 'Y': + queuedelay = (time_t) atol(&lbuf[1]); + break; +*/ +# endif /* _FFR_QUEUEDELAY */ } } (void) fclose(cf); @@ -1091,7 +1296,7 @@ orderq(doall) { /* don't even bother sorting this job in */ if (tTd(41, 49)) - printf("skipping %s (%x)\n", w->w_name, i); + dprintf("skipping %s (%x)\n", w->w_name, i); free(w->w_name); if (w->w_host) free(w->w_host); @@ -1101,15 +1306,15 @@ orderq(doall) (void) closedir(f); wn++; + WorkQ = NULL; + if (WorkList == NULL) + return 0; wc = min(wn, WorkListSize); if (wc > MaxQueueRun && MaxQueueRun > 0) wc = MaxQueueRun; - if (QueueSortOrder == QS_BYHOST) + if (QueueSortOrder == QSO_BYHOST) { - extern int workcmpf1(); - extern int workcmpf2(); - /* ** Sort the work directory for the first time, ** based on host name, lock status, and priority. @@ -1133,8 +1338,6 @@ orderq(doall) w = &WorkList[i]; while (++i < wc) { - extern int sm_strcasecmp __P((char *, char *)); - if (WorkList[i].w_host == NULL && w->w_host == NULL) WorkList[i].w_lock = TRUE; @@ -1154,20 +1357,24 @@ orderq(doall) qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); } - else if (QueueSortOrder == QS_BYTIME) + else if (QueueSortOrder == QSO_BYTIME) { - extern int workcmpf3(); - /* ** Simple sort based on submission time only. */ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); } - else + else if (QueueSortOrder == QSO_BYFILENAME) { - extern int workcmpf0(); + /* + ** Sort based on qf filename. + */ + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); + } + else + { /* ** Simple sort based on queue priority only. */ @@ -1180,7 +1387,6 @@ orderq(doall) ** Should be turning it into a list of envelopes here perhaps. */ - WorkQ = NULL; for (i = wc; --i >= 0; ) { w = (WORK *) xalloc(sizeof *w); @@ -1201,16 +1407,23 @@ orderq(doall) if (tTd(40, 1)) { for (w = WorkQ; w != NULL; w = w->w_next) - printf("%32s: pri=%ld\n", w->w_name, w->w_pri); + { + if (w->w_host != NULL) + dprintf("%22s: pri=%ld %s\n", + w->w_name, w->w_pri, w->w_host); + else + dprintf("%32s: pri=%ld\n", + w->w_name, w->w_pri); + } } - return (wn); + return wn; } /* ** GROW_WLIST -- make the work list larger ** ** Parameters: -** none. +** queuedir -- the index for the queue directory. ** ** Returns: ** none. @@ -1221,14 +1434,16 @@ orderq(doall) ** should be checked again upon return. */ -void -grow_wlist() +static void +grow_wlist(queuedir) + int queuedir; { if (tTd(41, 1)) - printf("grow_wlist: WorkListSize=%d\n", WorkListSize); + dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); if (WorkList == NULL) { - WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); + WorkList = (WORK *) xalloc((sizeof *WorkList) * + (QUEUESEGSIZE + 1)); WorkListSize = QUEUESEGSIZE; } else @@ -1243,20 +1458,21 @@ grow_wlist() WorkList = newlist; if (LogLevel > 1) { - sm_syslog(LOG_NOTICE, NOQID, - "grew WorkList for %s to %d", - QueueDir, WorkListSize); + sm_syslog(LOG_INFO, NOQID, + "grew WorkList for %s to %d", + qid_printqueue(queuedir), + WorkListSize); } } else if (LogLevel > 0) { sm_syslog(LOG_ALERT, NOQID, - "FAILED to grow WorkList for %s to %d", - QueueDir, newsize); + "FAILED to grow WorkList for %s to %d", + qid_printqueue(queuedir), newsize); } } if (tTd(41, 1)) - printf("grow_wlist: WorkListSize now %d\n", WorkListSize); + dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); } /* ** WORKCMPF0 -- simple priority-only compare function. @@ -1274,7 +1490,7 @@ grow_wlist() ** none. */ -int +static int workcmpf0(a, b) register WORK *a; register WORK *b; @@ -1307,13 +1523,12 @@ workcmpf0(a, b) ** none. */ -int +static int workcmpf1(a, b) register WORK *a; register WORK *b; { int i; - extern int sm_strcasecmp __P((char *, char *)); /* host name */ if (a->w_host != NULL && b->w_host == NULL) @@ -1349,13 +1564,12 @@ workcmpf1(a, b) ** none. */ -int +static int workcmpf2(a, b) register WORK *a; register WORK *b; { int i; - extern int sm_strcasecmp __P((char *, char *)); /* lock status */ if (a->w_lock != b->w_lock) @@ -1389,7 +1603,7 @@ workcmpf2(a, b) ** none. */ -int +static int workcmpf3(a, b) register WORK *a; register WORK *b; @@ -1402,9 +1616,61 @@ workcmpf3(a, b) return 0; } /* +** WORKCMPF4 -- compare based on file name +** +** Parameters: +** a -- the first argument. +** b -- the second argument. +** +** Returns: +** -1 if a < b +** 0 if a == b +** +1 if a > b +** +** Side Effects: +** none. +*/ + +static int +workcmpf4(a, b) + register WORK *a; + register WORK *b; +{ + return strcmp(a->w_name, b->w_name); +} +/* +** STRREV -- reverse string +** +** Returns a pointer to a new string that is the reverse of +** the string pointed to by fwd. The space for the new +** string is obtained using xalloc(). +** +** Parameters: +** fwd -- the string to reverse. +** +** Returns: +** the reversed string. +*/ + +static char * +strrev(fwd) + char *fwd; +{ + char *rev = NULL; + int len, cnt; + + len = strlen(fwd); + rev = xalloc(len + 1); + for (cnt = 0; cnt < len; ++cnt) + rev[cnt] = fwd[len - cnt - 1]; + rev[len] = '\0'; + return rev; +} +/* ** DOWORK -- do a work request. ** ** Parameters: +** queuedir -- the index of the queue directory for the job. ** id -- the ID of the job to run. ** forkflag -- if set, run this in background. ** requeueflag -- if set, reinstantiate the queue quickly. @@ -1421,17 +1687,17 @@ workcmpf3(a, b) */ pid_t -dowork(id, forkflag, requeueflag, e) +dowork(queuedir, id, forkflag, requeueflag, e) + int queuedir; char *id; bool forkflag; bool requeueflag; register ENVELOPE *e; { register pid_t pid; - extern bool readqf __P((ENVELOPE *)); if (tTd(40, 1)) - printf("dowork(%s)\n", id); + dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id); /* ** Fork for work. @@ -1439,6 +1705,16 @@ dowork(id, forkflag, requeueflag, e) if (forkflag) { + /* + ** Since the delivery may happen in a child and the + ** parent does not wait, the parent may close the + ** maps thereby removing any shared memory used by + ** the map. Therefore, close the maps now so the + ** child will dynamically open them if necessary. + */ + + closemaps(); + pid = fork(); if (pid < 0) { @@ -1454,16 +1730,6 @@ dowork(id, forkflag, requeueflag, e) { /* child -- error messages to the transcript */ QuickAbort = OnlyOneError = FALSE; - - /* - ** Since the delivery may happen in a child and the - ** parent does not wait, the parent may close the - ** maps thereby removing any shared memory used by - ** the map. Therefore, open a copy of the maps for - ** the delivery process. - */ - - initmaps(FALSE, e); } } else @@ -1483,23 +1749,25 @@ dowork(id, forkflag, requeueflag, e) /* set basic modes, etc. */ (void) alarm(0); + clearstats(); clearenvelope(e, FALSE); e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; - e->e_sendmode = SM_DELIVER; + set_delivery_mode(SM_DELIVER, e); e->e_errormode = EM_MAIL; e->e_id = id; + e->e_queuedir = queuedir; GrabTo = UseErrorsTo = FALSE; ExitStat = EX_OK; if (forkflag) { disconnect(1, e); - OpMode = MD_DELIVER; + OpMode = MD_QUEUERUN; } - sm_setproctitle(TRUE, "%s: from queue", id); + sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e)); if (LogLevel > 76) sm_syslog(LOG_DEBUG, e->e_id, - "dowork, pid=%d", - getpid()); + "dowork, pid=%d", + getpid()); /* don't use the headers from sendmail.cf... */ e->e_header = NULL; @@ -1508,7 +1776,8 @@ dowork(id, forkflag, requeueflag, e) if (!readqf(e)) { if (tTd(40, 4) && e->e_id != NULL) - printf("readqf(%s) failed\n", e->e_id); + dprintf("readqf(%s) failed\n", + qid_printname(e)); e->e_id = NULL; if (forkflag) finis(FALSE, EX_OK); @@ -1548,7 +1817,7 @@ dowork(id, forkflag, requeueflag, e) ** The queue file is returned locked. */ -bool +static bool readqf(e) register ENVELOPE *e; { @@ -1561,22 +1830,26 @@ readqf(e) register char *p; char *orcpt = NULL; bool nomore = FALSE; - char qf[MAXQFNAME]; + MODE_T qsafe; + char qf[MAXPATHLEN]; char buf[MAXLINE]; - extern ADDRESS *setctluser __P((char *, int)); /* ** Read and process the file. */ - strcpy(qf, queuename(e, 'q')); + (void) strlcpy(qf, queuename(e, 'q'), sizeof qf); qfp = fopen(qf, "r+"); if (qfp == NULL) { + int save_errno = errno; + if (tTd(40, 8)) - printf("readqf(%s): fopen failure (%s)\n", + dprintf("readqf(%s): fopen failure (%s)\n", qf, errstring(errno)); - if (errno != ENOENT) + errno = save_errno; + if (errno != ENOENT + ) syserr("readqf: no control file %s", qf); return FALSE; } @@ -1584,8 +1857,10 @@ readqf(e) if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) { /* being processed by another queuer */ - if (Verbose || tTd(40, 8)) + if (Verbose) printf("%s: locked\n", e->e_id); + if (tTd(40, 8)) + dprintf("%s: locked\n", e->e_id); if (LogLevel > 19) sm_syslog(LOG_DEBUG, e->e_id, "locked"); (void) fclose(qfp); @@ -1600,25 +1875,33 @@ readqf(e) { /* must have been being processed by someone else */ if (tTd(40, 8)) - printf("readqf(%s): fstat failure (%s)\n", + dprintf("readqf(%s): fstat failure (%s)\n", qf, errstring(errno)); - fclose(qfp); + (void) fclose(qfp); return FALSE; } - if ((st.st_uid != geteuid() && geteuid() != RealUid) || - bitset(S_IWOTH|S_IWGRP, st.st_mode)) + qsafe = S_IWOTH|S_IWGRP; +#if _FFR_QUEUE_FILE_MODE + if (bitset(S_IWGRP, QueueFileMode)) + qsafe &= ~S_IWGRP; +#endif /* _FFR_QUEUE_FILE_MODE */ + + if ((st.st_uid != geteuid() && + st.st_uid != TrustedUid && + geteuid() != RealUid) || + bitset(qsafe, st.st_mode)) { if (LogLevel > 0) { sm_syslog(LOG_ALERT, e->e_id, - "bogus queue file, uid=%d, mode=%o", - st.st_uid, st.st_mode); + "bogus queue file, uid=%d, mode=%o", + st.st_uid, st.st_mode); } if (tTd(40, 8)) - printf("readqf(%s): bogus file\n", qf); + dprintf("readqf(%s): bogus file\n", qf); loseqfile(e, "bogus file uid in mqueue"); - fclose(qfp); + (void) fclose(qfp); return FALSE; } @@ -1627,12 +1910,10 @@ readqf(e) /* must be a bogus file -- if also old, just remove it */ if (st.st_ctime + 10 * 60 < curtime()) { - qf[0] = 'd'; - (void) unlink(qf); - qf[0] = 'q'; - (void) unlink(qf); + (void) xunlink(queuename(e, 'd')); + (void) xunlink(queuename(e, 'q')); } - fclose(qfp); + (void) fclose(qfp); return FALSE; } @@ -1643,7 +1924,7 @@ readqf(e) ** unlinked. Just assume it is zero length. */ - fclose(qfp); + (void) fclose(qfp); return FALSE; } @@ -1656,25 +1937,28 @@ readqf(e) LineNumber = 0; e->e_flags |= EF_GLOBALERRS; - OpMode = MD_DELIVER; + OpMode = MD_QUEUERUN; ctladdr = NULL; e->e_dfino = -1; e->e_msgsize = -1; +# if _FFR_QUEUEDELAY + e->e_queuealg = QD_LINEAR; + e->e_queuedelay = (time_t) 0; +# endif /* _FFR_QUEUEDELAY */ while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) { - register char *p; u_long qflags; ADDRESS *q; int mid; auto char *ep; if (tTd(40, 4)) - printf("+++++ %s\n", bp); + dprintf("+++++ %s\n", bp); if (nomore) { /* hack attack */ syserr("SECURITY ALERT: extra data in qf: %s", bp); - fclose(qfp); + (void) fclose(qfp); loseqfile(e, "bogus queue line"); return FALSE; } @@ -1686,7 +1970,7 @@ readqf(e) break; syserr("Version number in qf (%d) greater than max (%d)", qfver, QF_VERSION); - fclose(qfp); + (void) fclose(qfp); loseqfile(e, "unsupported qf file version"); return FALSE; @@ -1750,7 +2034,7 @@ readqf(e) break; case 'H': /* header */ - (void) chompheader(&bp[1], FALSE, NULL, e); + (void) chompheader(&bp[1], 0, NULL, e); hdrsize += strlen(&bp[1]); break; @@ -1767,11 +2051,11 @@ readqf(e) e->e_bodytype = newstr(&bp[1]); break; -#if _FFR_SAVE_CHARSET +# if _FFR_SAVE_CHARSET case 'X': /* character set */ e->e_charset = newstr(&bp[1]); break; -#endif +# endif /* _FFR_SAVE_CHARSET */ case 'D': /* data file name */ /* obsolete -- ignore */ @@ -1785,31 +2069,58 @@ readqf(e) /* regenerated below */ break; - case 'K': /* time of last deliver attempt */ + case 'K': /* time of last delivery attempt */ e->e_dtime = atol(&buf[1]); break; +# if _FFR_QUEUEDELAY + case 'G': /* queue delay algorithm */ + e->e_queuealg = atoi(&buf[1]); + break; + case 'Y': /* current delay */ + e->e_queuedelay = (time_t) atol(&buf[1]); + break; +# endif /* _FFR_QUEUEDELAY */ + case 'N': /* number of delivery attempts */ e->e_ntries = atoi(&buf[1]); /* if this has been tried recently, let it be */ - if (e->e_ntries > 0 && - MinQueueAge > 0 && e->e_dtime <= curtime() && - curtime() < e->e_dtime + MinQueueAge) + if (e->e_ntries > 0 && e->e_dtime <= curtime() && + curtime() < e->e_dtime + queuedelay(e)) { - char *howlong = pintvl(curtime() - e->e_dtime, TRUE); + char *howlong; - if (Verbose || tTd(40, 8)) + howlong = pintvl(curtime() - e->e_dtime, TRUE); + if (Verbose) printf("%s: too young (%s)\n", + e->e_id, howlong); + if (tTd(40, 8)) + dprintf("%s: too young (%s)\n", e->e_id, howlong); if (LogLevel > 19) sm_syslog(LOG_DEBUG, e->e_id, - "too young (%s)", - howlong); + "too young (%s)", + howlong); e->e_id = NULL; unlockqueue(e); return FALSE; } + define(macid("{ntries}", NULL), newstr(&buf[1]), e); + +# if NAMED_BIND + /* adjust BIND parameters immediately */ + if (e->e_ntries == 0) + { + _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; + _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; + } + else + { + _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; + _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; + } +# endif /* NAMED_BIND */ break; case 'P': /* message priority */ @@ -1821,7 +2132,7 @@ readqf(e) { /* we are being spoofed! */ syserr("SECURITY ALERT: bogus qf line %s", bp); - fclose(qfp); + (void) fclose(qfp); loseqfile(e, "bogus queue line"); return FALSE; } @@ -1858,11 +2169,40 @@ readqf(e) case 'Z': /* original envelope id from ESMTP */ e->e_envid = newstr(&bp[1]); + define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e); + break; + + case 'A': /* AUTH= parameter */ + e->e_auth_param = newstr(&bp[1]); break; case '$': /* define macro */ - mid = macid(&bp[1], &ep); - define(mid, newstr(ep), e); + { + char *p; + + mid = macid(&bp[1], &ep); + p = newstr(ep); + define(mid, p, e); + + /* + ** HACK ALERT: Unfortunately, 8.10 and + ** 8.11 reused the ${if_addr} and + ** ${if_family} macros for both the incoming + ** interface address/family (getrequests()) + ** and the outgoing interface address/family + ** (makeconnection()). In order for D_BINDIF + ** to work properly, have to preserve the + ** incoming information in the queue file for + ** later delivery attempts. The original + ** information is stored in the envelope + ** in readqf() so it can be stored in + ** queueup_macros(). This should be fixed + ** in 8.12. + */ + + if (strcmp(macname(mid), "if_addr") == 0) + e->e_if_macros[EIF_ADDR] = p; + } break; case '.': /* terminate file */ @@ -1872,7 +2212,7 @@ readqf(e) default: syserr("readqf: %s: line %d: bad line \"%s\"", qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); - fclose(qfp); + (void) fclose(qfp); loseqfile(e, "unrecognized line"); return FALSE; } @@ -1893,6 +2233,15 @@ readqf(e) return TRUE; } + /* possibly set ${dsn_ret} macro */ + if (bitset(EF_RET_PARAM, e->e_flags)) + { + if (bitset(EF_NO_BODY_RETN, e->e_flags)) + define(macid("{dsn_ret}", NULL), "hdrs", e); + else + define(macid("{dsn_ret}", NULL), "full", e); + } + /* ** Arrange to read the data file. */ @@ -1917,6 +2266,48 @@ readqf(e) return TRUE; } /* +** PRTSTR -- print a string, "unprintable" characters are shown as \oct +** +** Parameters: +** s -- string to print +** ml -- maximum length of output +** +** Returns: +** none. +** +** Side Effects: +** Prints a string on stdout. +*/ + +static void +prtstr(s, ml) + char *s; + int ml; +{ + char c; + + if (s == NULL) + return; + while (ml-- > 0 && ((c = *s++) != '\0')) + { + if (c == '\\') + { + if (ml-- > 0) + { + putchar(c); + putchar(c); + } + } + else if (isascii(c) && isprint(c)) + putchar(c); + else + { + if ((ml -= 3) > 0) + printf("\\%03o", c); + } + } +} +/* ** PRINTQUEUE -- print out a representation of the mail queue ** ** Parameters: @@ -1932,11 +2323,52 @@ readqf(e) void printqueue() { + int i, nrequests = 0; + + for (i = 0; i < NumQueues; i++) + nrequests += print_single_queue(i); + if (NumQueues > 1) + printf("\t\tTotal Requests: %d\n", nrequests); +} +/* +** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue +** +** Parameters: +** queuedir -- queue directory +** +** Returns: +** none. +** +** Side Effects: +** Prints a listing of the mail queue on the standard output. +*/ + +static int +print_single_queue(queuedir) + int queuedir; +{ register WORK *w; FILE *f; int nrequests; + char qd[MAXPATHLEN]; + char qddf[MAXPATHLEN]; char buf[MAXLINE]; + if (queuedir == NOQDIR) + { + (void) strlcpy(qd, ".", sizeof qd); + (void) strlcpy(qddf, ".", sizeof qddf); + } + else + { + (void) snprintf(qd, sizeof qd, "%s%s", + QPaths[queuedir].qp_name, + (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); + (void) snprintf(qddf, sizeof qddf, "%s%s", + QPaths[queuedir].qp_name, + (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); + } + /* ** Check for permission to print the queue */ @@ -1947,12 +2379,12 @@ printqueue() # ifdef NGROUPS_MAX int n; extern GIDSET_T InitialGidSet[NGROUPS_MAX]; -# endif +# endif /* NGROUPS_MAX */ - if (stat(QueueDir, &st) < 0) + if (stat(qd, &st) < 0) { - syserr("Cannot stat %s", QueueDir); - return; + syserr("Cannot stat %s", qid_printqueue(queuedir)); + return 0; } # ifdef NGROUPS_MAX n = NGROUPS_MAX; @@ -1962,13 +2394,13 @@ printqueue() break; } if (n < 0 && RealGid != st.st_gid) -# else +# else /* NGROUPS_MAX */ if (RealGid != st.st_gid) -# endif +# endif /* NGROUPS_MAX */ { usrerr("510 You are not permitted to see the queue"); setstat(EX_NOPERM); - return; + return 0; } } @@ -1976,7 +2408,7 @@ printqueue() ** Read and order the queue. */ - nrequests = orderq(TRUE); + nrequests = orderq(queuedir, TRUE); /* ** Print the work list that we have read. @@ -1985,19 +2417,20 @@ printqueue() /* first see if there is anything */ if (nrequests <= 0) { - printf("Mail queue is empty\n"); - return; + printf("%s is empty\n", qid_printqueue(queuedir)); + return 0; } - CurrentLA = getla(); /* get load average */ + CurrentLA = sm_getla(NULL); /* get load average */ - printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); + printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests, + nrequests == 1 ? "" : "s"); if (MaxQueueRun > 0 && nrequests > MaxQueueRun) printf(", only %d printed", MaxQueueRun); if (Verbose) - printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); + printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n"); else - printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); + printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); for (w = WorkQ; w != NULL; w = w->w_next) { struct stat st; @@ -2007,9 +2440,11 @@ printqueue() int qfver; char statmsg[MAXLINE]; char bodytype[MAXNAME + 1]; + char qf[MAXPATHLEN]; - printf("%8s", w->w_name + 2); - f = fopen(w->w_name, "r"); + printf("%12s", w->w_name + 2); + (void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name); + f = fopen(qf, "r"); if (f == NULL) { printf(" (job completed)\n"); @@ -2017,7 +2452,8 @@ printqueue() continue; } w->w_name[0] = 'd'; - if (stat(w->w_name, &st) >= 0) + (void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name); + if (stat(qf, &st) >= 0) dfsize = st.st_size; else dfsize = -1; @@ -2048,42 +2484,47 @@ printqueue() case 'M': /* error message */ if ((i = strlen(&buf[1])) >= sizeof statmsg) i = sizeof statmsg - 1; - bcopy(&buf[1], statmsg, i); + memmove(statmsg, &buf[1], i); statmsg[i] = '\0'; break; case 'B': /* body type */ if ((i = strlen(&buf[1])) >= sizeof bodytype) i = sizeof bodytype - 1; - bcopy(&buf[1], bodytype, i); + memmove(bodytype, &buf[1], i); bodytype[i] = '\0'; break; case 'S': /* sender name */ if (Verbose) - printf("%8ld %10ld%c%.12s %.78s", - dfsize, - w->w_pri, - bitset(EF_WARNING, flags) ? '+' : ' ', - ctime(&submittime) + 4, - &buf[1]); + { + printf("%8ld %10ld%c%.12s ", + dfsize, + w->w_pri, + bitset(EF_WARNING, flags) ? '+' : ' ', + ctime(&submittime) + 4); + prtstr(&buf[1], 78); + } else - printf("%8ld %.16s %.45s", dfsize, - ctime(&submittime), &buf[1]); + { + printf("%8ld %.16s ", dfsize, + ctime(&submittime)); + prtstr(&buf[1], 40); + } if (statmsg[0] != '\0' || bodytype[0] != '\0') { printf("\n %10.10s", bodytype); if (statmsg[0] != '\0') printf(" (%.*s)", - Verbose ? 100 : 60, - statmsg); + Verbose ? 100 : 60, + statmsg); } break; case 'C': /* controlling user */ if (Verbose) printf("\n\t\t\t\t (---%.74s---)", - &buf[1]); + &buf[1]); break; case 'R': /* recipient name */ @@ -2096,9 +2537,15 @@ printqueue() p++; } if (Verbose) - printf("\n\t\t\t\t\t %.78s", p); + { + printf("\n\t\t\t\t\t "); + prtstr(p, 73); + } else - printf("\n\t\t\t\t %.45s", p); + { + printf("\n\t\t\t\t "); + prtstr(p, 40); + } break; case 'T': /* creation time */ @@ -2122,148 +2569,173 @@ printqueue() printf("\n"); (void) fclose(f); } + return nrequests; } - -# endif /* QUEUE */ /* ** QUEUENAME -- build a file name in the queue directory for this envelope. ** -** Assigns an id code if one does not already exist. -** This code is very careful to avoid trashing existing files -** under any circumstances. -** ** Parameters: ** e -- envelope to build it in/from. ** type -- the file type, used as the first character ** of the file name. ** ** Returns: -** a pointer to the new file name (in a static buffer). +** a pointer to the queue name (in a static buffer). ** ** Side Effects: -** If no id code is already assigned, queuename will -** assign an id code, create a qf file, and leave a -** locked, open-for-write file pointer in the envelope. +** If no id code is already assigned, queuename() will +** assign an id code with assign_queueid(). If no queue +** directory is assigned, one will be set with setnewqueue(). */ -#ifndef ENOLCK -# define ENOLCK -1 -#endif -#ifndef ENOSPC -# define ENOSPC -1 -#endif - char * queuename(e, type) register ENVELOPE *e; int type; { - static pid_t pid = -1; - static char c0; - static char c1; - static char c2; - time_t now; - struct tm *tm; - static char buf[MAXNAME + 1]; + char *sub = ""; + static char buf[MAXPATHLEN]; + /* Assign an ID if needed */ if (e->e_id == NULL) - { - char qf[MAXQFNAME]; + assign_queueid(e); + + /* Assign a queue directory if needed */ + if (e->e_queuedir == NOQDIR) + setnewqueue(e); - /* find a unique id */ - if (pid != getpid()) + if (e->e_queuedir == NOQDIR) + (void) snprintf(buf, sizeof buf, "%cf%s", + type, e->e_id); + else + { + switch (type) { - /* new process -- start back at "AA" */ - pid = getpid(); - now = curtime(); - tm = localtime(&now); - c0 = 'A' + tm->tm_hour; - c1 = 'A'; - c2 = 'A' - 1; + case 'd': + if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs)) + sub = "/df"; + break; + + case 'T': + case 't': + case 'Q': + case 'q': + if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs)) + sub = "/qf"; + break; + + case 'x': + if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs)) + sub = "/xf"; + break; } - (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid); - while (c1 < '~' || c2 < 'Z') - { - int i; - int attempts = 0; + (void) snprintf(buf, sizeof buf, "%s%s/%cf%s", + QPaths[e->e_queuedir].qp_name, + sub, type, e->e_id); + } - if (c2 >= 'Z') - { - c1++; - c2 = 'A' - 1; - } - qf[3] = c1; - qf[4] = ++c2; - if (tTd(7, 20)) - printf("queuename: trying \"%s\"\n", qf); + if (tTd(7, 2)) + dprintf("queuename: %s\n", buf); + return buf; +} +/* +** ASSIGN_QUEUEID -- assign a queue ID for this envelope. +** +** Assigns an id code if one does not already exist. +** This code assumes that nothing will remain in the queue for +** longer than 60 years. It is critical that files with the given +** name not already exist in the queue. +** Also initializes e_queuedir to NOQDIR. +** +** Parameters: +** e -- envelope to set it in. +** +** Returns: +** none. +*/ - i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); - if (i < 0) - { - if (errno == EEXIST) - continue; - syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_UNAVAILABLE); - } - do - { - if (attempts > 0) - sleep(attempts); - e->e_lockfp = 0; - if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) - { - e->e_lockfp = fdopen(i, "w"); - break; - } - } while ((errno == ENOLCK || errno == ENOSPC) && - attempts++ < 4); +static char Base60Code[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; - /* Successful lock */ - if (e->e_lockfp != 0) - break; +void +assign_queueid(e) + register ENVELOPE *e; +{ + pid_t pid = getpid(); + static char cX = 0; + static long random_offset; + struct tm *tm; + char idbuf[MAXQFNAME - 2]; -#if !HASFLOCK - if (errno != EAGAIN && errno != EACCES) -#else - if (errno != EWOULDBLOCK) -#endif - { - syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_OSERR); - } + if (e->e_id != NULL) + return; - /* a reader got the file; abandon it and try again */ - (void) close(i); - } - if (c1 >= '~' && c2 >= 'Z') - { - syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_OSERR); - } - e->e_id = newstr(&qf[2]); - define('i', e->e_id, e); - if (tTd(7, 1)) - printf("queuename: assigned id %s, env=%lx\n", - e->e_id, (u_long) e); - if (tTd(7, 9)) + /* see if we need to get a new base time/pid */ + if (cX >= 60 || LastQueueTime == 0 || LastQueuePid != pid) + { + time_t then = LastQueueTime; + + /* if the first time through, pick a random offset */ + if (LastQueueTime == 0) + random_offset = get_random(); + + while ((LastQueueTime = curtime()) == then && + LastQueuePid == pid) { - printf(" lockfd="); - dumpfd(fileno(e->e_lockfp), TRUE, FALSE); + (void) sleep(1); } - if (LogLevel > 93) - sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); + LastQueuePid = getpid(); + cX = 0; } - - if (type == '\0') - return (NULL); - (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id); - if (tTd(7, 2)) - printf("queuename: %s\n", buf); - return (buf); + if (tTd(7, 50)) + dprintf("assign_queueid: random_offset = %ld (%d)\n", + random_offset, (int)(cX + random_offset) % 60); + + tm = gmtime(&LastQueueTime); + idbuf[0] = Base60Code[tm->tm_year % 60]; + idbuf[1] = Base60Code[tm->tm_mon]; + idbuf[2] = Base60Code[tm->tm_mday]; + idbuf[3] = Base60Code[tm->tm_hour]; + idbuf[4] = Base60Code[tm->tm_min]; + idbuf[5] = Base60Code[tm->tm_sec]; + idbuf[6] = Base60Code[((int)cX++ + random_offset) % 60]; + (void) snprintf(&idbuf[7], sizeof idbuf - 7, "%05d", + (int) LastQueuePid); + e->e_id = newstr(idbuf); + define('i', e->e_id, e); + e->e_queuedir = NOQDIR; + if (tTd(7, 1)) + dprintf("assign_queueid: assigned id %s, e=%lx\n", + e->e_id, (u_long) e); + if (LogLevel > 93) + sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); +} +/* +** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second +** +** Make sure one PID can't be used by two processes in any one second. +** +** If the system rotates PIDs fast enough, may get the +** same pid in the same second for two distinct processes. +** This will interfere with the queue file naming system. +** +** Parameters: +** none +** +** Returns: +** none +*/ +void +sync_queue_time() +{ +# if FAST_PID_RECYCLE + if (OpMode != MD_TEST && + OpMode != MD_VERIFY && + LastQueueTime > 0 && + LastQueuePid == getpid() && + curtime() == LastQueueTime) + (void) sleep(1); +# endif /* FAST_PID_RECYCLE */ } /* ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope @@ -2283,12 +2755,13 @@ unlockqueue(e) ENVELOPE *e; { if (tTd(51, 4)) - printf("unlockqueue(%s)\n", + dprintf("unlockqueue(%s)\n", e->e_id == NULL ? "NOQUEUE" : e->e_id); + /* if there is a lock file in the envelope, close it */ if (e->e_lockfp != NULL) - xfclose(e->e_lockfp, "unlockqueue", e->e_id); + (void) fclose(e->e_lockfp); e->e_lockfp = NULL; /* don't create a queue id if we don't already have one */ @@ -2319,7 +2792,7 @@ unlockqueue(e) ** none. */ -ADDRESS * +static ADDRESS * setctluser(user, qfver) char *user; int qfver; @@ -2340,7 +2813,7 @@ setctluser(user, qfver) */ a = (ADDRESS *) xalloc(sizeof *a); - bzero((char *) a, sizeof *a); + memset((char *) a, '\0', sizeof *a); if (*user == '\0') { @@ -2377,10 +2850,10 @@ setctluser(user, qfver) } } - a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ + a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ a->q_mailer = LocalMailer; if (p == NULL) - a->q_paddr = a->q_user; + a->q_paddr = newstr(a->q_user); else a->q_paddr = newstr(p); return a; @@ -2396,27 +2869,500 @@ setctluser(user, qfver) ** none. */ +# define LOSEQF_LETTER 'Q' + void loseqfile(e, why) register ENVELOPE *e; char *why; { char *p; - char buf[MAXQFNAME + 1]; + char buf[MAXPATHLEN]; if (e == NULL || e->e_id == NULL) return; p = queuename(e, 'q'); - if (strlen(p) > MAXQFNAME) - { - syserr("loseqfile: queuename (%s) too long", p); + if (strlen(p) >= (SIZE_T) sizeof buf) return; - } - strcpy(buf, p); - p = queuename(e, 'Q'); + (void) strlcpy(buf, p, sizeof buf); + p = queuename(e, LOSEQF_LETTER); if (rename(buf, p) < 0) syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); else if (LogLevel > 0) sm_syslog(LOG_ALERT, e->e_id, - "Losing %s: %s", buf, why); + "Losing %s: %s", buf, why); +} +/* +** QID_PRINTNAME -- create externally printable version of queue id +** +** Parameters: +** e -- the envelope. +** +** Returns: +** a printable version +*/ + +char * +qid_printname(e) + ENVELOPE *e; +{ + char *id; + static char idbuf[MAXQFNAME + 34]; + + if (e == NULL) + return ""; + + if (e->e_id == NULL) + id = ""; + else + id = e->e_id; + + if (e->e_queuedir == NOQDIR) + return id; + + (void) snprintf(idbuf, sizeof idbuf, "%.32s/%s", + QPaths[e->e_queuedir].qp_name, id); + return idbuf; +} +/* +** QID_PRINTQUEUE -- create full version of queue directory for df files +** +** Parameters: +** queuedir -- the short version of the queue directory +** +** Returns: +** the full pathname to the queue (static) +*/ + +char * +qid_printqueue(queuedir) + int queuedir; +{ + char *subdir; + static char dir[MAXPATHLEN]; + + if (queuedir == NOQDIR) + return QueueDir; + + if (strcmp(QPaths[queuedir].qp_name, ".") == 0) + subdir = NULL; + else + subdir = QPaths[queuedir].qp_name; + + (void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir, + subdir == NULL ? "" : "/", + subdir == NULL ? "" : subdir, + (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); + return dir; +} +/* +** SETNEWQUEUE -- Sets a new queue directory +** +** Assign a queue directory to an envelope and store the directory +** in e->e_queuedir. The queue is chosen at random. +** +** This routine may be improved in the future to allow for more +** elaborate queueing schemes. Suggestions and code contributions +** are welcome. +** +** Parameters: +** e -- envelope to assign a queue for. +** +** Returns: +** none. +*/ + +void +setnewqueue(e) + ENVELOPE *e; +{ + int idx; + + if (tTd(41, 20)) + dprintf("setnewqueue: called\n"); + + if (e->e_queuedir != NOQDIR) + { + if (tTd(41, 20)) + dprintf("setnewqueue: e_queuedir already assigned (%s)\n", + qid_printqueue(e->e_queuedir)); + return; + } + + if (NumQueues == 1) + idx = 0; + else + { +#if RANDOMSHIFT + /* lower bits are not random "enough", select others */ + idx = (get_random() >> RANDOMSHIFT) % NumQueues; +#else /* RANDOMSHIFT */ + idx = get_random() % NumQueues; +#endif /* RANDOMSHIFT */ + if (tTd(41, 15)) + dprintf("setnewqueue: get_random() %% %d = %d\n", + NumQueues, idx); + } + + e->e_queuedir = idx; + if (tTd(41, 3)) + dprintf("setnewqueue: Assigned queue directory %s\n", + qid_printqueue(e->e_queuedir)); +} + +/* +** CHKQDIR -- check a queue directory +** +** Parameters: +** name -- name of queue directory +** sff -- flags for safefile() +** +** Returns: +** is it a queue directory? +*/ + +static bool +chkqdir(name, sff) + char *name; + long sff; +{ + struct stat statb; + int i; + +# if HASLSTAT + if (lstat(name, &statb) < 0) +# else /* HASLSTAT */ + if (stat(name, &statb) < 0) +# endif /* HASLSTAT */ + { + if (tTd(41, 2)) + dprintf("multiqueue_cache: stat(\"%s\"): %s\n", + name, errstring(errno)); + return FALSE; + } +# if HASLSTAT + if (S_ISLNK(statb.st_mode)) + { + /* + ** For a symlink we need to make sure the + ** target is a directory + */ + if (stat(name, &statb) < 0) + { + if (tTd(41, 2)) + dprintf("multiqueue_cache: stat(\"%s\"): %s\n", + name, errstring(errno)); + return FALSE; + } + } +# endif /* HASLSTAT */ + + if (!S_ISDIR(statb.st_mode)) + { + if (tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": Not a directory\n", + name); + return FALSE; + } + + /* Print a warning if unsafe (but still use it) */ + i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); + if (i != 0 && tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", + name, errstring(i)); + return TRUE; +} + +/* +** MULTIQUEUE_CACHE -- cache a list of paths to queues. +** +** Each potential queue is checked as the cache is built. +** Thereafter, each is blindly trusted. +** Note that we can be called again after a timeout to rebuild +** (although code for that is not ready yet). +** +** Parameters: +** none +** +** Returns: +** none +*/ + +void +multiqueue_cache() +{ + register DIR *dp; + register struct dirent *d; + char *cp; + int i, len; + int slotsleft = 0; + long sff = SFF_ANYFILE; + char qpath[MAXPATHLEN]; + char subdir[MAXPATHLEN]; + + if (tTd(41, 20)) + dprintf("multiqueue_cache: called\n"); + + if (NumQueues != 0 && QPaths != NULL) + { + for (i = 0; i < NumQueues; i++) + { + if (QPaths[i].qp_name != NULL) + (void) free(QPaths[i].qp_name); + } + (void) free((char *)QPaths); + QPaths = NULL; + NumQueues = 0; + } + + /* If running as root, allow safedirpath() checks to use privs */ + if (RunAsUid == 0) + sff |= SFF_ROOTOK; + + (void) snprintf(qpath, sizeof qpath, "%s", QueueDir); + len = strlen(qpath) - 1; + cp = &qpath[len]; + if (*cp == '*') + { + *cp = '\0'; + if ((cp = strrchr(qpath, '/')) == NULL) + { + syserr("QueueDirectory: can not wildcard relative path"); + if (tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n", + QueueDir); + ExitStat = EX_CONFIG; + return; + } + if (cp == qpath) + { + /* + ** Special case of top level wildcard, like /foo* + */ + + (void) snprintf(qpath + 1, sizeof qpath - 1, + "%s", qpath); + ++cp; + } + *(cp++) = '\0'; + len = strlen(cp); + + if (tTd(41, 2)) + dprintf("multiqueue_cache: prefix=\"%s\"\n", cp); + + QueueDir = newstr(qpath); + + /* + ** XXX Should probably wrap this whole loop in a timeout + ** in case some wag decides to NFS mount the queues. + */ + + /* test path to get warning messages */ + i= safedirpath(QueueDir, RunAsUid, RunAsGid, NULL, sff, 0, 0); + if (i != 0 && tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", + QueueDir, errstring(i)); + + if (chdir(QueueDir) < 0) + { + syserr("can not chdir(%s)", QueueDir); + if (tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": %s\n", + qpath, errstring(errno)); + ExitStat = EX_CONFIG; + return; + } + + if ((dp = opendir(".")) == NULL) + { + syserr("can not opendir(%s)", QueueDir); + if (tTd(41, 2)) + dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", + QueueDir, errstring(errno)); + ExitStat = EX_CONFIG; + return; + } + while ((d = readdir(dp)) != NULL) + { + if (strncmp(d->d_name, cp, len) != 0) + { + if (tTd(41, 5)) + dprintf("multiqueue_cache: \"%s\", skipped\n", + d->d_name); + continue; + } + if (!chkqdir(d->d_name, sff)) + continue; + + if (QPaths == NULL) + { + slotsleft = 20; + QPaths = (QPATHS *)xalloc((sizeof *QPaths) * + slotsleft); + NumQueues = 0; + } + else if (slotsleft < 1) + { + QPaths = (QPATHS *)realloc((char *)QPaths, + (sizeof *QPaths) * + (NumQueues + 10)); + if (QPaths == NULL) + { + (void) closedir(dp); + return; + } + slotsleft += 10; + } + + /* check subdirs */ + QPaths[NumQueues].qp_subdirs = QP_NOSUB; + (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", + qpath, d->d_name, "qf"); + if (chkqdir(subdir, sff)) + QPaths[NumQueues].qp_subdirs |= QP_SUBQF; + + (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", + qpath, d->d_name, "df"); + if (chkqdir(subdir, sff)) + QPaths[NumQueues].qp_subdirs |= QP_SUBDF; + + (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", + qpath, d->d_name, "xf"); + if (chkqdir(subdir, sff)) + QPaths[NumQueues].qp_subdirs |= QP_SUBXF; + + /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ + /* maybe even - 17 (subdirs) */ + QPaths[NumQueues].qp_name = newstr(d->d_name); + if (tTd(41, 2)) + dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", + NumQueues, d->d_name, + QPaths[NumQueues].qp_subdirs); + NumQueues++; + slotsleft--; + } + (void) closedir(dp); + } + if (NumQueues == 0) + { + if (*cp != '*' && tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": No wildcard suffix character\n", + QueueDir); + QPaths = (QPATHS *)xalloc(sizeof *QPaths); + QPaths[0].qp_name = newstr("."); + QPaths[0].qp_subdirs = QP_NOSUB; + NumQueues = 1; + + /* test path to get warning messages */ + (void) safedirpath(QueueDir, RunAsUid, RunAsGid, + NULL, sff, 0, 0); + if (chdir(QueueDir) < 0) + { + syserr("can not chdir(%s)", QueueDir); + if (tTd(41, 2)) + dprintf("multiqueue_cache: \"%s\": %s\n", + QueueDir, errstring(errno)); + ExitStat = EX_CONFIG; + } + + /* check subdirs */ + (void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir); + if (chkqdir(subdir, sff)) + QPaths[0].qp_subdirs |= QP_SUBQF; + + (void) snprintf(subdir, sizeof subdir, "%s/df", QueueDir); + if (chkqdir(subdir, sff)) + QPaths[0].qp_subdirs |= QP_SUBDF; + + (void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir); + if (chkqdir(subdir, sff)) + QPaths[0].qp_subdirs |= QP_SUBXF; + } +} + +# if 0 +/* +** HASHFQN -- calculate a hash value for a fully qualified host name +** +** Arguments: +** fqn -- an all lower-case host.domain string +** buckets -- the number of buckets (queue directories) +** +** Returns: +** a bucket number (signed integer) +** -1 on error +** +** Contributed by Exactis.com, Inc. +*/ + +int +hashfqn(fqn, buckets) + register char *fqn; + int buckets; +{ + register char *p; + register int h = 0, hash, cnt; +# define WATERINC (1000) + + if (fqn == NULL) + return -1; + + /* + ** A variation on the gdb hash + ** This is the best as of Feb 19, 1996 --bcx + */ + + p = fqn; + h = 0x238F13AF * strlen(p); + for (cnt = 0; *p != 0; ++p, cnt++) + { + h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; + } + h = (1103515243 * h + 12345) & 0x7FFFFFFF; + if (buckets < 2) + hash = 0; + else + hash = (h % buckets); + + return hash; +} +# endif /* 0 */ + +# if _FFR_QUEUEDELAY +/* +** QUEUEDELAY -- compute queue delay time +** +** Parameters: +** e -- the envelope to queue up. +** +** Returns: +** queue delay time +** +** Side Effects: +** may change e_queuedelay +*/ + +static time_t +queuedelay(e) + ENVELOPE *e; +{ + time_t qd; + + if (e->e_queuealg == QD_EXP) + { + if (e->e_queuedelay == 0) + e->e_queuedelay = QueueInitDelay; + else + { + e->e_queuedelay *= 2; + if (e->e_queuedelay > QueueMaxDelay) + e->e_queuedelay = QueueMaxDelay; + } + qd = e->e_queuedelay; + } + else + qd = MinQueueAge; + return qd; } +# endif /* _FFR_QUEUEDELAY */ +#endif /* QUEUE */ |