📄 queue.c
字号:
"dowork, pid=%d",
getpid());
/* don't use the headers from sendmail.cf... */
e->e_header = NULL;
/* read the queue control file -- return if locked */
if (!readqf(e))
{
if (tTd(40, 4) && e->e_id != NULL)
dprintf("readqf(%s) failed\n",
qid_printname(e));
e->e_id = NULL;
if (forkflag)
finis(FALSE, EX_OK);
else
return 0;
}
e->e_flags |= EF_INQUEUE;
eatheader(e, requeueflag);
if (requeueflag)
queueup(e, FALSE);
/* do the delivery */
sendall(e, SM_DELIVER);
/* finish up and exit */
if (forkflag)
finis(TRUE, ExitStat);
else
dropenvelope(e, TRUE);
}
e->e_id = NULL;
return pid;
}
/*
** READQF -- read queue file and set up environment.
**
** Parameters:
** e -- the envelope of the job to run.
**
** Returns:
** TRUE if it successfully read the queue file.
** FALSE otherwise.
**
** Side Effects:
** The queue file is returned locked.
*/
static bool
readqf(e)
register ENVELOPE *e;
{
register FILE *qfp;
ADDRESS *ctladdr;
struct stat st;
char *bp;
int qfver = 0;
long hdrsize = 0;
register char *p;
char *orcpt = NULL;
bool nomore = FALSE;
MODE_T qsafe;
char qf[MAXPATHLEN];
char buf[MAXLINE];
/*
** Read and process the file.
*/
(void) strlcpy(qf, queuename(e, 'q'), sizeof qf);
qfp = fopen(qf, "r+");
if (qfp == NULL)
{
int save_errno = errno;
if (tTd(40, 8))
dprintf("readqf(%s): fopen failure (%s)\n",
qf, errstring(errno));
errno = save_errno;
if (errno != ENOENT
)
syserr("readqf: no control file %s", qf);
return FALSE;
}
if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
{
/* being processed by another queuer */
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);
return FALSE;
}
/*
** Check the queue file for plausibility to avoid attacks.
*/
if (fstat(fileno(qfp), &st) < 0)
{
/* must have been being processed by someone else */
if (tTd(40, 8))
dprintf("readqf(%s): fstat failure (%s)\n",
qf, errstring(errno));
(void) fclose(qfp);
return FALSE;
}
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);
}
if (tTd(40, 8))
dprintf("readqf(%s): bogus file\n", qf);
loseqfile(e, "bogus file uid in mqueue");
(void) fclose(qfp);
return FALSE;
}
if (st.st_size == 0)
{
/* must be a bogus file -- if also old, just remove it */
if (st.st_ctime + 10 * 60 < curtime())
{
(void) xunlink(queuename(e, 'd'));
(void) xunlink(queuename(e, 'q'));
}
(void) fclose(qfp);
return FALSE;
}
if (st.st_nlink == 0)
{
/*
** Race condition -- we got a file just as it was being
** unlinked. Just assume it is zero length.
*/
(void) fclose(qfp);
return FALSE;
}
/* good file -- save this lock */
e->e_lockfp = qfp;
/* do basic system initialization */
initsys(e);
define('i', e->e_id, e);
LineNumber = 0;
e->e_flags |= EF_GLOBALERRS;
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)
{
u_long qflags;
ADDRESS *q;
int mid;
auto char *ep;
if (tTd(40, 4))
dprintf("+++++ %s\n", bp);
if (nomore)
{
/* hack attack */
syserr("SECURITY ALERT: extra data in qf: %s", bp);
(void) fclose(qfp);
loseqfile(e, "bogus queue line");
return FALSE;
}
switch (bp[0])
{
case 'V': /* queue file version number */
qfver = atoi(&bp[1]);
if (qfver <= QF_VERSION)
break;
syserr("Version number in qf (%d) greater than max (%d)",
qfver, QF_VERSION);
(void) fclose(qfp);
loseqfile(e, "unsupported qf file version");
return FALSE;
case 'C': /* specify controlling user */
ctladdr = setctluser(&bp[1], qfver);
break;
case 'Q': /* original recipient */
orcpt = newstr(&bp[1]);
break;
case 'R': /* specify recipient */
p = bp;
qflags = 0;
if (qfver >= 1)
{
/* get flag bits */
while (*++p != '\0' && *p != ':')
{
switch (*p)
{
case 'N':
qflags |= QHASNOTIFY;
break;
case 'S':
qflags |= QPINGONSUCCESS;
break;
case 'F':
qflags |= QPINGONFAILURE;
break;
case 'D':
qflags |= QPINGONDELAY;
break;
case 'P':
qflags |= QPRIMARY;
break;
}
}
}
else
qflags |= QPRIMARY;
q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
if (q != NULL)
{
q->q_alias = ctladdr;
if (qfver >= 1)
q->q_flags &= ~Q_PINGFLAGS;
q->q_flags |= qflags;
q->q_orcpt = orcpt;
(void) recipient(q, &e->e_sendqueue, 0, e);
}
orcpt = NULL;
break;
case 'E': /* specify error recipient */
/* no longer used */
break;
case 'H': /* header */
(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
hdrsize += strlen(&bp[1]);
break;
case 'L': /* Solaris Content-Length: */
case 'M': /* message */
/* ignore this; we want a new message next time */
break;
case 'S': /* sender */
setsender(newstr(&bp[1]), e, NULL, '\0', TRUE);
break;
case 'B': /* body type */
e->e_bodytype = newstr(&bp[1]);
break;
# if _FFR_SAVE_CHARSET
case 'X': /* character set */
e->e_charset = newstr(&bp[1]);
break;
# endif /* _FFR_SAVE_CHARSET */
case 'D': /* data file name */
/* obsolete -- ignore */
break;
case 'T': /* init time */
e->e_ctime = atol(&bp[1]);
break;
case 'I': /* data file's inode number */
/* regenerated below */
break;
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 && e->e_dtime <= curtime() &&
curtime() < e->e_dtime + queuedelay(e))
{
char *howlong;
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);
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 */
e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
break;
case 'F': /* flag bits */
if (strncmp(bp, "From ", 5) == 0)
{
/* we are being spoofed! */
syserr("SECURITY ALERT: bogus qf line %s", bp);
(void) fclose(qfp);
loseqfile(e, "bogus queue line");
return FALSE;
}
for (p = &bp[1]; *p != '\0'; p++)
{
switch (*p)
{
case 'w': /* warning sent */
e->e_flags |= EF_WARNING;
break;
case 'r': /* response */
e->e_flags |= EF_RESPONSE;
break;
case '8': /* has 8 bit data */
e->e_flags |= EF_HAS8BIT;
break;
case 'b': /* delete Bcc: header */
e->e_flags |= EF_DELETE_BCC;
break;
case 'd': /* envelope has DSN RET= */
e->e_flags |= EF_RET_PARAM;
break;
case 'n': /* don't return body */
e->e_flags |= EF_NO_BODY_RETN;
break;
}
}
break;
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 */
{
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 */
nomore = TRUE;
break;
default:
syserr("readqf: %s: line %d: bad line \"%s\"",
qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
(void) fclose(qfp);
loseqfile(e, "unrecognized line");
return FALSE;
}
if (bp != buf)
free(bp);
}
/*
** If we haven't read any lines, this queue file is empty.
** Arrange to remove it without referencing any null pointers.
*/
if (LineNumber == 0)
{
errno = 0;
e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
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.
*/
p = queuename(e, 'd');
e->e_dfp = fopen(p, "r");
if (e->e_dfp == NULL)
{
syserr("readqf: cannot open %s", p);
}
else
{
e->e_flags |= EF_HAS_DF;
if (fstat(fileno(e->e_dfp), &st) >= 0)
{
e->e_msgsize = st.st_size + hdrsize;
e->e_dfdev = st.st_dev;
e->e_dfino = st.st_ino;
}
}
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:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Prints a listing of the mail queue on the standard output.
*/
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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -