diff options
Diffstat (limited to 'contrib/sendmail/src/queue.c')
-rw-r--r-- | contrib/sendmail/src/queue.c | 436 |
1 files changed, 347 insertions, 89 deletions
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c index 6a2da9c..cb40c98 100644 --- a/contrib/sendmail/src/queue.c +++ b/contrib/sendmail/src/queue.c @@ -13,7 +13,7 @@ #include <sendmail.h> -SM_RCSID("@(#)$Id: queue.c,v 8.834 2002/01/08 23:04:58 ca Exp $") +SM_RCSID("@(#)$Id: queue.c,v 8.857 2002/04/02 16:43:25 ca Exp $") #include <dirent.h> @@ -71,13 +71,11 @@ static WORK *WorkQ; /* queue of things to be done */ static int NumWorkGroups; /* number of work groups */ /* -** use of DoQueueRun: -** NumQueue: indicates that a queue run is needed, look at individual bits -** 0 - NumQueue-1: indicates that a queue run for this queue group -** is needed. +** DoQueueRun indicates that a queue run is needed. +** Notice: DoQueueRun is modified in a signal handler! */ -static BITMAP256 volatile DoQueueRun; /* non-interrupt time queue run needed */ +static bool volatile DoQueueRun; /* non-interrupt time queue run needed */ /* ** Work group definition structure. @@ -124,7 +122,7 @@ static void printctladdr __P((ADDRESS *, SM_FILE_T *)); static bool readqf __P((ENVELOPE *, bool)); static void restart_work_group __P((int)); static void runner_work __P((ENVELOPE *, int, bool, int, int)); -static void schedule_queue_runs __P((bool, int)); +static void schedule_queue_runs __P((bool, int, bool)); static char *strrev __P((char *)); static ADDRESS *setctluser __P((char *, int, ENVELOPE *)); #if _FFR_RHS @@ -1217,6 +1215,7 @@ restart_work_group(wgrp) ** Parameters: ** runall -- schedule even if individual bit is not set. ** wgrp -- the work group id to schedule. +** didit -- the queue run was performed for this work group. ** ** Returns: ** nothing @@ -1227,22 +1226,35 @@ restart_work_group(wgrp) else static void -schedule_queue_runs(runall, wgrp) +schedule_queue_runs(runall, wgrp, didit) bool runall; int wgrp; + bool didit; { int qgrp, cgrp, endgrp; +#if _FFR_QUEUE_SCHED_DBG + time_t lastsched; + bool sched; +#endif /* _FFR_QUEUE_SCHED_DBG */ + time_t now; + time_t minqintvl; /* ** This is a bit ugly since we have to duplicate the ** code that "walks" through a work queue group. */ + now = curtime(); + minqintvl = 0; cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; do { time_t qintvl; +#if _FFR_QUEUE_SCHED_DBG + lastsched = 0; + sched = false; +#endif /* _FFR_QUEUE_SCHED_DBG */ qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; if (Queue[qgrp]->qg_queueintvl > 0) qintvl = Queue[qgrp]->qg_queueintvl; @@ -1250,21 +1262,97 @@ schedule_queue_runs(runall, wgrp) qintvl = QueueIntvl; else qintvl = (time_t) 0; - if ((runall || bitnset(qgrp, DoQueueRun)) && qintvl > 0) - (void) sm_setevent(qintvl, runqueueevent, qgrp); +#if _FFR_QUEUE_SCHED_DBG + lastsched = Queue[qgrp]->qg_nextrun; +#endif /* _FFR_QUEUE_SCHED_DBG */ + if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0) + { +#if _FFR_QUEUE_SCHED_DBG + sched = true; +#endif /* _FFR_QUEUE_SCHED_DBG */ + if (minqintvl == 0 || qintvl < minqintvl) + minqintvl = qintvl; + + /* + ** Only set a new time if a queue run was performed + ** for this queue group. If the queue was not run, + ** we could starve it by setting a new time on each + ** call. + */ + + if (didit) + Queue[qgrp]->qg_nextrun += qintvl; + } #if _FFR_QUEUE_SCHED_DBG if (tTd(69, 10)) sm_syslog(LOG_INFO, NOQID, - "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, bit=%d, sched=%d", + "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d", wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl, - QueueIntvl, runall, bitnset(qgrp, DoQueueRun), - (runall || bitnset(qgrp, DoQueueRun)) && - qintvl > 0); + QueueIntvl, runall, lastsched, + Queue[qgrp]->qg_nextrun, sched); #endif /* _FFR_QUEUE_SCHED_DBG */ - clrbitn(qgrp, DoQueueRun); INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); } while (endgrp != cgrp); + if (minqintvl > 0) + (void) sm_setevent(minqintvl, runqueueevent, 0); } + +#if _FFR_QUEUE_RUN_PARANOIA +/* +** CHECKQUEUERUNNER -- check whether a queue group hasn't been run. +** +** Use this if events may get lost and hence queue runners may not +** be started and mail will pile up in a queue. +** +** Parameters: +** none. +** +** Returns: +** true if a queue run is necessary. +** +** Side Effects: +** may schedule a queue run. +*/ + +bool +checkqueuerunner() +{ + int qgrp; + time_t now, minqintvl; + + now = curtime(); + minqintvl = 0; + for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) + { + time_t qintvl; + + if (Queue[qgrp]->qg_queueintvl > 0) + qintvl = Queue[qgrp]->qg_queueintvl; + else if (QueueIntvl > 0) + qintvl = QueueIntvl; + else + qintvl = (time_t) 0; + if (Queue[qgrp]->qg_nextrun <= now - qintvl) + { + if (minqintvl == 0 || qintvl < minqintvl) + minqintvl = qintvl; + if (LogLevel > 1) + sm_syslog(LOG_WARNING, NOQID, + "checkqueuerunner: queue %d should have been run at %s, queue interval %ld", + qgrp, + arpadate(ctime(&Queue[qgrp]->qg_nextrun)), + qintvl); + } + } + if (minqintvl > 0) + { + (void) sm_setevent(minqintvl, runqueueevent, 0); + return true; + } + return false; +} +#endif /* _FFR_QUEUE_RUN_PARANOIA */ + /* ** RUNQUEUE -- run the jobs in the queue. ** @@ -1286,7 +1374,6 @@ schedule_queue_runs(runall, wgrp) ** Side Effects: ** runs things in the mail queue using run_work_group(). ** maybe schedules next queue run. -** */ static ENVELOPE QueueEnvelope; /* the queue run envelope */ @@ -1322,7 +1409,7 @@ runqueue(forkflag, verbose, persistent, runall) #endif /* SM_HEAP_CHECK */ /* queue run has been started, don't do any more this time */ - clrbitn(NumQueue, DoQueueRun); + DoQueueRun = false; /* more than one queue or more than one directory per queue */ if (!forkflag && !verbose && @@ -1392,7 +1479,7 @@ runqueue(forkflag, verbose, persistent, runall) /* Success means the runner count needs to be updated. */ CurRunners += WorkGrp[curnum].wg_maxact; if (!persistent) - schedule_queue_runs(runall, curnum); + schedule_queue_runs(runall, curnum, true); INCR_MOD(curnum, NumWorkGroups); } @@ -1403,7 +1490,7 @@ runqueue(forkflag, verbose, persistent, runall) for (h = curnum; i < NumWorkGroups; i++) { - schedule_queue_runs(runall, h); + schedule_queue_runs(runall, h, false); INCR_MOD(h, NumWorkGroups); } } @@ -1638,7 +1725,7 @@ run_work_group(wgrp, forkflag, verbose, persistent, runall) int njobs, qdir; int sequenceno = 1; int qgrp, endgrp, h, i; - time_t current_la_time; + time_t current_la_time, now; bool full, more; SM_RPOOL_T *rpool; extern void rmexpstab __P((void)); @@ -1809,6 +1896,7 @@ run_work_group(wgrp, forkflag, verbose, persistent, runall) ** runall is set or the bit for this group is set. */ + now = curtime(); for (;;) { /* @@ -1819,7 +1907,9 @@ run_work_group(wgrp, forkflag, verbose, persistent, runall) qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; WorkGrp[wgrp].wg_curqgrp++; /* advance */ WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ - if (runall || bitnset(qgrp, DoQueueRun)) + if (runall || + (Queue[qgrp]->qg_nextrun <= now && + Queue[qgrp]->qg_nextrun != (time_t) -1)) break; if (endgrp == WorkGrp[wgrp].wg_curqgrp) { @@ -2039,8 +2129,6 @@ run_work_group(wgrp, forkflag, verbose, persistent, runall) /* No more queues in work group to process. Now check persistent. */ if (persistent) { - time_t now; - sequenceno = 1; sm_setproctitle(true, CurEnv, "running queue: %s", qid_printqueue(qgrp, qdir)); @@ -2134,25 +2222,19 @@ run_work_group(wgrp, forkflag, verbose, persistent, runall) bool doqueuerun() { - return bitnset(NumQueue, DoQueueRun); + return DoQueueRun; } /* -** RUNQUEUEEVENT -- stub for use in sm_setevent -** -** Sets the bit to indicate that on the next run this queue should be -** processed. The work group that the queue group is a member of has its -** count of queue's to process updated. +** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. ** ** Parameters: -** qgrp -- the index of the queue group. +** none. ** ** Returns: ** none. ** ** Side Effects: -** The work group that the queue group is a member of has its -** count of queues to process updated. ** The invocation of this function via an alarm may interrupt ** a set of actions. Thus errno may be set in that context. ** We need to restore errno at the end of this function to ensure @@ -2168,10 +2250,8 @@ doqueuerun() */ void -runqueueevent(qgrp) - int qgrp; +runqueueevent() { - int i; int save_errno = errno; /* @@ -2179,23 +2259,12 @@ runqueueevent(qgrp) ** tested in doqueuerun() */ - setbitn(NumQueue, DoQueueRun); - - /* if it is a specific group: set that bit */ - if (qgrp != NOQGRP) - { - setbitn(qgrp, DoQueueRun); - goto ret; - } - - /* for all others: set the bit if it doesn't have a queue interval */ - for (i = 0; i < NumQueue; i++) - { - if (Queue[i]->qg_queueintvl <= 0) - setbitn(i, DoQueueRun); - } + DoQueueRun = true; +#if _FFR_QUEUE_SCHED_DBG + if (tTd(69, 10)) + sm_syslog(LOG_INFO, NOQID, "rqe: done"); +#endif /* _FFR_QUEUE_SCHED_DBG */ - ret: errno = save_errno; if (errno == EINTR) errno = ETIMEDOUT; @@ -2374,7 +2443,7 @@ gatherq(qgrp, qdir, doall, full, more) check = QueueLimitId; while (check != NULL) { - if (strcontainedin(true, check->queue_match, + if (strcontainedin(false, check->queue_match, d->d_name) != check->queue_negate) break; else @@ -3689,6 +3758,7 @@ readqf(e, openonly) bool nomore = false; bool bogus = false; MODE_T qsafe; + char *err; char qf[MAXPATHLEN]; char buf[MAXLINE]; @@ -3934,13 +4004,8 @@ readqf(e, openonly) hackattack: syserr("SECURITY ALERT: extra or bogus data in queue file: %s", bp); - (void) sm_io_close(qfp, SM_TIME_DEFAULT); - - /* the file is already on disk */ - e->e_flags |= EF_INQUEUE; - loseqfile(e, "bogus queue line"); - RELEASE_QUEUE; - return false; + err = "bogus queue line"; + goto fail; } switch (bp[0]) { @@ -3992,9 +4057,8 @@ readqf(e, openonly) } } } - loseqfile(e, "bogus queue file directory"); - RELEASE_QUEUE; - return false; + err = "bogus queue file directory"; + goto fail; done: break; } @@ -4008,10 +4072,8 @@ readqf(e, openonly) { /* we are being spoofed! */ syserr("SECURITY ALERT: bogus qf line %s", bp); - (void) sm_io_close(qfp, SM_TIME_DEFAULT); - loseqfile(e, "bogus queue line"); - RELEASE_QUEUE; - return false; + err = "bogus queue line"; + goto fail; } for (p = &bp[1]; *p != '\0'; p++) { @@ -4063,8 +4125,15 @@ readqf(e, openonly) #endif /* _FFR_QUARANTINE */ case 'H': /* header */ + + /* + ** count size before chompheader() destroys the line. + ** this isn't accurate due to macro expansion, but + ** better than before. "+3" to skip H?? at least. + */ + + hdrsize += strlen(bp + 3); (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); - hdrsize += strlen(&bp[1]); break; case 'I': /* data file's inode number */ @@ -4184,6 +4253,9 @@ readqf(e, openonly) true); if (q != NULL) { + /* make sure we keep the current qgrp */ + if (ISVALIDQGRP(e->e_qgrp)) + q->q_qgrp = e->e_qgrp; q->q_alias = ctladdr; if (qfver >= 1) q->q_flags &= ~Q_PINGFLAGS; @@ -4215,10 +4287,8 @@ readqf(e, openonly) break; syserr("Version number in queue file (%d) greater than max (%d)", qfver, QF_VERSION); - (void) sm_io_close(qfp, SM_TIME_DEFAULT); - loseqfile(e, "unsupported queue file version"); - RELEASE_QUEUE; - return false; + err = "unsupported queue file version"; + goto fail; /* NOTREACHED */ break; @@ -4260,10 +4330,8 @@ readqf(e, openonly) default: syserr("readqf: %s: line %d: bad line \"%s\"", qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); - (void) sm_io_close(qfp, SM_TIME_DEFAULT); - loseqfile(e, "unrecognized line"); - RELEASE_QUEUE; - return false; + err = "unrecognized line"; + goto fail; } if (bp != buf) @@ -4328,6 +4396,24 @@ readqf(e, openonly) RELEASE_QUEUE; return true; + + fail: + /* + ** There was some error reading the qf file (reason is in err var.) + ** Cleanup: + ** close file; clear e_lockfp since it is the same as qfp, + ** hence it is invalid (as file) after qfp is closed; + ** the qf file is on disk, so set the flag to avoid calling + ** queueup() with bogus data. + */ + + if (qfp != NULL) + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + e->e_lockfp = NULL; + e->e_flags |= EF_INQUEUE; + loseqfile(e, err); + RELEASE_QUEUE; + return false; } /* ** PRTSTR -- print a string, "unprintable" characters are shown as \oct @@ -4987,7 +5073,8 @@ queuename(e, type) } } - if (e->e_qdir == NOQDIR) + /* xf files always have a valid qd and qg picked above */ + if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER) (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id); else { @@ -6289,6 +6376,98 @@ upd_qs(e, delete, avail) FILE_SYS_AVAIL(fidx) -= s; } + +#if _FFR_SELECT_SHM + +static bool write_key_file __P((char *, long)); +static long read_key_file __P((char *, long)); + +/* +** WRITE_KEY_FILE -- record some key into a file. +** +** Parameters: +** keypath -- file name. +** key -- key to write. +** +** Returns: +** true iff file could be written. +** +** Side Effects: +** writes file. +*/ + +static bool +write_key_file(keypath, key) + char *keypath; + long key; +{ + bool ok; + long sff; + SM_FILE_T *keyf; + + ok = false; + if (keypath == NULL || *keypath == '\0') + return ok; + sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; + if (TrustedUid != 0 && RealUid == TrustedUid) + sff |= SFF_OPENASROOT; + keyf = safefopen(keypath, O_WRONLY|O_TRUNC, 0644, sff); + if (keyf == NULL) + { + sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", + keypath, sm_errstring(errno)); + } + else + { + ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != + SM_IO_EOF; + ok = ok && (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF); + } + return ok; +} + +/* +** READ_KEY_FILE -- read a key from a file. +** +** Parameters: +** keypath -- file name. +** key -- default key. +** +** Returns: +** key. +*/ + +static long +read_key_file(keypath, key) + char *keypath; + long key; +{ + int r; + long sff, n; + SM_FILE_T *keyf; + + if (keypath == NULL || *keypath == '\0') + return key; + sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; + if (TrustedUid != 0 && RealUid == TrustedUid) + sff |= SFF_OPENASROOT; + keyf = safefopen(keypath, O_RDONLY, 0644, sff); + if (keyf == NULL) + { + sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", + keypath, sm_errstring(errno)); + } + else + { + r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); + if (r == 1) + key = n; + (void) sm_io_close(keyf, SM_TIME_DEFAULT); + } + return key; +} +#endif /* _FFR_SELECT_SHM */ + /* ** INIT_SHM -- initialize shared memory structure ** @@ -6316,9 +6495,17 @@ init_shm(qn, owner, hash) unsigned int hash; { int i; +#if _FFR_SELECT_SHM + bool keyselect; +#endif /* _FFR_SELECT_SHM */ PtrFileSys = &FileSys[0]; PNumFileSys = &Numfilesys; +#if _FFR_SELECT_SHM +/* if this "key" is specified: select one yourself */ +# define SEL_SHM_KEY ((key_t) -1) +# define FIRST_SHM_KEY 25 +#endif /* _FFR_SELECT_SHM */ /* This allows us to disable shared memory at runtime. */ if (ShmKey != 0) @@ -6329,6 +6516,21 @@ init_shm(qn, owner, hash) count = 0; shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); +#if _FFR_SELECT_SHM + keyselect = ShmKey == SEL_SHM_KEY; + if (keyselect) + { + if (owner) + ShmKey = FIRST_SHM_KEY; + else + { + ShmKey = read_key_file(ShmKeyFile, ShmKey); + keyselect = false; + if (ShmKey == SEL_SHM_KEY) + goto error; + } + } +#endif /* _FFR_SELECT_SHM */ for (;;) { /* XXX: maybe allow read access for group? */ @@ -6338,13 +6540,34 @@ init_shm(qn, owner, hash) if (Pshm != NULL || save_errno != EEXIST) break; if (++count >= 3) + { +#if _FFR_SELECT_SHM + if (keyselect) + { + ++ShmKey; + + /* back where we started? */ + if (ShmKey == SEL_SHM_KEY) + break; + continue; + } +#endif /* _FFR_SELECT_SHM */ break; + } +#if _FFR_SELECT_SHM + /* only sleep if we are at the first key */ + if (!keyselect || ShmKey == SEL_SHM_KEY) +#endif /* _FFR_SELECT_SHM */ sleep(count); } if (Pshm != NULL) { int *p; +#if _FFR_SELECT_SHM + if (keyselect) + (void) write_key_file(ShmKeyFile, (long) ShmKey); +#endif /* _FFR_SELECT_SHM */ p = (int *) Pshm; if (owner) { @@ -6425,6 +6648,7 @@ setup_queues(owner) { int i, qn, len; unsigned int hashval; + time_t now; char basedir[MAXPATHLEN]; struct stat st; @@ -6494,8 +6718,11 @@ setup_queues(owner) hashval = hash_q(basedir, hashval); #endif /* SM_CONF_SHM */ - /* initialize map for queue runs */ - clrbitmap(DoQueueRun); + /* initialize for queue runs */ + DoQueueRun = false; + now = curtime(); + for (i = 0; i < NumQueue && Queue[i] != NULL; i++) + Queue[i]->qg_nextrun = now; if (UseMSP && OpMode != MD_TEST) @@ -6645,9 +6872,9 @@ set_def_queueval(qg, all) return; if (all) qg->qg_qdir = QueueDir; -#if 0 +#if _FFR_QUEUE_GROUP_SORTORDER qg->qg_sortorder = QueueSortOrder; -#endif /* 0 */ +#endif /* _FFR_QUEUE_GROUP_SORTORDER */ qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; qg->qg_nice = NiceQueueRun; } @@ -6788,7 +7015,7 @@ makequeue(line, qdef) qg->qg_maxrcpt = atoi(p); break; -#if 0 +#if _FFR_QUEUE_GROUP_SORTORDER case 'S': /* queue sorting order */ switch (*p) { @@ -6814,14 +7041,26 @@ makequeue(line, qdef) case 'm': /* Modification time */ case 'M': - qgrp->qg_sortorder = QSO_BYMODTIME; + qg->qg_sortorder = QSO_BYMODTIME; + break; + + case 'r': /* Random */ + case 'R': + qg->qg_sortorder = QSO_RANDOM; + break; + +# if _FFR_RHS + case 's': /* Shuffled host name */ + case 'S': + qg->qg_sortorder = QSO_BYSHUFFLE; break; +# endif /* _FFR_RHS */ default: syserr("Invalid queue sort order \"%s\"", p); } break; -#endif /* 0 */ +#endif /* _FFR_QUEUE_GROUP_SORTORDER */ default: syserr("Q%s: unknown queue equate %c=", @@ -7117,12 +7356,16 @@ makeworkgroups() dir = 1; } - WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, - sizeof(QUEUEGRP *) * - (WorkGrp[j].wg_numqgrp + 1)); + if (WorkGrp[j].wg_qgs == NULL) + WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * + (WorkGrp[j].wg_numqgrp + 1)); + else + WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, + sizeof(QUEUEGRP *) * + (WorkGrp[j].wg_numqgrp + 1)); if (WorkGrp[j].wg_qgs == NULL) { - syserr("@cannot allocate memory for work queues, need %d bytes", + syserr("!cannot allocate memory for work queues, need %d bytes", (int) (sizeof(QUEUEGRP *) * (WorkGrp[j].wg_numqgrp + 1))); } @@ -7203,7 +7446,15 @@ dup_df(old, new) char opath[MAXPATHLEN]; char npath[MAXPATHLEN]; - SM_REQUIRE(bitset(EF_HAS_DF, old->e_flags)); + if (!bitset(EF_HAS_DF, old->e_flags)) + { + /* + ** this can happen if: SuperSafe != True + ** and a bounce mail is sent that is split. + */ + + queueup(old, false, true); + } SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); @@ -7305,6 +7556,11 @@ split_env(e, sendqueue, qgrp, qdir) ee->e_lockfp = NULL; if (e->e_xfp != NULL) ee->e_xfp = sm_io_dup(e->e_xfp); + + /* failed to dup e->e_xfp, start a new transcript */ + if (ee->e_xfp == NULL) + openxscript(ee); + ee->e_qgrp = ee->e_dfqgrp = qgrp; ee->e_qdir = ee->e_dfqdir = qdir; ee->e_errormode = EM_MAIL; @@ -7317,7 +7573,7 @@ split_env(e, sendqueue, qgrp, qdir) /* ** XXX Not sure if this copying is necessary. - ** sendall() does this copying, but I don't know if that is + ** sendall() does this copying, but I (dm) don't know if that is ** because of the storage management discipline we were using ** before rpools were introduced, or if it is because these lists ** can be modified later. @@ -7444,6 +7700,8 @@ split_across_queue_groups(e) if (q->q_mailer != NULL && ISVALIDQGRP(q->q_mailer->m_qgrp)) q->q_qgrp = q->q_mailer->m_qgrp; + else if (ISVALIDQGRP(e->e_qgrp)) + q->q_qgrp = e->e_qgrp; else q->q_qgrp = 0; } |