📄 queue.c
字号:
/*
* 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.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#include <sendmail.h>
#ifndef lint
# if QUEUE
static char id[] = "@(#)$Id: queue.c,v 8.343.4.17 2000/09/15 03:34:51 gshapiro Exp $ (with queueing)";
# else /* QUEUE */
static char id[] = "@(#)$Id: queue.c,v 8.343.4.17 2000/09/15 03:34:51 gshapiro Exp $ (without queueing)";
# endif /* QUEUE */
#endif /* ! lint */
# include <dirent.h>
#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.
*/
struct work
{
char *w_name; /* name of control file */
char *w_host; /* name of recipient host */
bool w_lock; /* is message locked? */
bool w_tooyoung; /* is it too young to run? */
long w_pri; /* priority of message, see below */
time_t w_ctime; /* creation time of message */
struct work *w_next; /* next in queue */
};
typedef struct work WORK;
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();
/*
** QUEUEUP -- queue a message up for future transmission.
**
** Parameters:
** e -- the envelope to queue up.
** announce -- if TRUE, tell when you are queueing up.
**
** Returns:
** none.
**
** Side Effects:
** The current request are saved in a control file.
** The queue file is left locked.
*/
# define TEMPQF_LETTER 'T'
void
queueup(e, announce)
register ENVELOPE *e;
bool announce;
{
char *qf;
register FILE *tfp;
register HDR *h;
register ADDRESS *q;
int tfd = -1;
int i;
bool newid;
register char *p;
MAILER nullmailer;
MCI mcibuf;
char tf[MAXPATHLEN];
char buf[MAXLINE];
/*
** Create control file.
*/
newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
/* if newid, queuename will create a locked qf file in e->lockfp */
(void) strlcpy(tf, queuename(e, 't'), sizeof tf);
tfp = e->e_lockfp;
if (tfp == NULL)
newid = FALSE;
/* 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++)
{
if (tfd < 0)
{
#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));
}
}
if (tfd >= 0)
{
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));
if ((i % 32) == 31)
{
(void) close(tfd);
tfd = -1;
}
}
if ((i % 32) == 31)
{
/* save the old temp file away */
(void) rename(tf, queuename(e, TEMPQF_LETTER));
}
else
(void) sleep(i % 32);
}
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))
dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n",
qid_printqueue(e->e_queuedir), e->e_id,
newid ? " (new id)" : "");
if (tTd(40, 3))
{
dprintf(" e_flags=");
printenvflags(e);
}
if (tTd(40, 32))
{
dprintf(" sendq=");
printaddr(e->e_sendqueue, TRUE);
}
if (tTd(40, 9))
{
dprintf(" tfp=");
dumpfd(fileno(tfp), TRUE, FALSE);
dprintf(" lockfp=");
if (e->e_lockfp == NULL)
dprintf("NULL\n");
else
dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
}
/*
** If there is no data file yet, create one.
*/
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[MAXPATHLEN];
struct stat stbuf;
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(dfd, &stbuf) < 0)
e->e_dfino = -1;
else
{
e->e_dfdev = stbuf.st_dev;
e->e_dfino = stbuf.st_ino;
}
e->e_flags |= EF_HAS_DF;
memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_out = dfp;
mcibuf.mci_mailer = FileMailer;
(*e->e_putbody)(&mcibuf, e, NULL);
if (fclose(dfp) < 0)
syserr("!queueup: cannot save data temp file %s, uid=%d",
dfname, geteuid());
e->e_putbody = putbody;
}
/*
** Output future work requests.
** Priority and creation time should be first, since
** they are required by orderq.
*/
/* output queue version number (must be first!) */
fprintf(tfp, "V%d\n", QF_VERSION);
/* output creation time */
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);
/* output message priority */
fprintf(tfp, "P%ld\n", e->e_msgpriority);
/* output inode number of data file */
/* XXX should probably include device major/minor too */
if (e->e_dfino != -1)
{
/*CONSTCOND*/
if (sizeof e->e_dfino > sizeof(long))
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%ld/%ld/%lu\n",
(long) major(e->e_dfdev),
(long) minor(e->e_dfdev),
(unsigned long) e->e_dfino);
}
/* output body type */
if (e->e_bodytype != NULL)
fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
# if _FFR_SAVE_CHARSET
if (e->e_charset != NULL)
fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
# endif /* _FFR_SAVE_CHARSET */
/* message from envelope, if it exists */
if (e->e_message != NULL)
fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
/* send various flag bits through */
p = buf;
if (bitset(EF_WARNING, e->e_flags))
*p++ = 'w';
if (bitset(EF_RESPONSE, e->e_flags))
*p++ = 'r';
if (bitset(EF_HAS8BIT, e->e_flags))
*p++ = '8';
if (bitset(EF_DELETE_BCC, e->e_flags))
*p++ = 'b';
if (bitset(EF_RET_PARAM, e->e_flags))
*p++ = 'd';
if (bitset(EF_NO_BODY_RETN, e->e_flags))
*p++ = 'n';
*p++ = '\0';
if (buf[0] != '\0')
fprintf(tfp, "F%s\n", buf);
/* 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))
p = e->e_sender;
else
p = e->e_from.q_paddr;
fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
/* output ESMTP-supplied "original" information */
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 (!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));
(void) putc('R', tfp);
if (bitset(QPRIMARY, q->q_flags))
(void) putc('P', tfp);
if (bitset(QHASNOTIFY, q->q_flags))
(void) putc('N', tfp);
if (bitset(QPINGONSUCCESS, q->q_flags))
(void) putc('S', tfp);
if (bitset(QPINGONFAILURE, q->q_flags))
(void) putc('F', tfp);
if (bitset(QPINGONDELAY, q->q_flags))
(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, q->q_status,
"queued", NULL, (time_t) 0, e);
e->e_to = NULL;
}
if (tTd(40, 1))
{
dprintf("queueing ");
printaddr(q, FALSE);
}
}
/*
** Output headers for this message.
** Expand macros completely here. Queue run will deal with
** everything as absolute headers.
** All headers that must be relative to the recipient
** can be cracked later.
** We set up a "null mailer" -- i.e., a mailer that will have
** no effect on the addresses as they are output.
*/
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";
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)
{
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))
continue;
/* expand macros; if null, don't output header at all */
if (bitset(H_DEFAULT, h->h_flags))
{
(void) expand(h->h_value, buf, sizeof buf, e);
if (buf[0] == '\0')
continue;
}
/* output this header */
fprintf(tfp, "H?");
/* 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;
/* 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);
/* output the header: expand macros, convert addresses */
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) &&
!bitset(H_BINDLATE, h->h_flags))
{
bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
FILE *savetrace = TrafficLogFile;
TrafficLogFile = NULL;
if (bitset(H_FROM, h->h_flags))
oldstyle = FALSE;
commaize(h, h->h_value, oldstyle, &mcibuf, e);
TrafficLogFile = savetrace;
}
else
{
fprintf(tfp, "%s: %s\n",
h->h_field,
denlstring(h->h_value, FALSE, TRUE));
}
}
/*
** Clean up.
**
** Write a terminator record -- this is to prevent
** scurrilous crackers from appending any data.
*/
fprintf(tfp, ".\n");
if (fflush(tfp) < 0 ||
(SuperSafe && fsync(fileno(tfp)) < 0) ||
ferror(tfp))
{
if (newid)
syserr("!552 Error writing control file %s", tf);
else
syserr("!452 Error writing control file %s", tf);
}
if (!newid)
{
/* rename (locked) tf to be (locked) qf */
qf = queuename(e, 'q');
if (rename(tf, qf) < 0)
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) fclose(e->e_lockfp);
e->e_lockfp = tfp;
}
else
qf = tf;
errno = 0;
e->e_flags |= EF_INQUEUE;
/* save log info */
if (LogLevel > 79)
sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
if (tTd(40, 1))
dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
return;
}
static void
printctladdr(a, tfp)
register ADDRESS *a;
FILE *tfp;
{
char *user;
register ADDRESS *q;
uid_t uid;
gid_t gid;
static ADDRESS *lastctladdr = NULL;
static uid_t lastuid;
/* initialization */
if (a == NULL || a->q_alias == NULL || tfp == NULL)
{
if (lastctladdr != NULL && tfp != NULL)
fprintf(tfp, "C\n");
lastctladdr = NULL;
lastuid = 0;
return;
}
/* find the active uid */
q = getctladdr(a);
if (q == NULL)
{
user = NULL;
uid = 0;
gid = 0;
}
else
{
user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
uid = q->q_uid;
gid = q->q_gid;
}
a = a->q_alias;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -