📄 deliver.c
字号:
ee->e_flags |= EF_INQUEUE;
dropenvelope(ee, FALSE);
}
return;
case SM_FORK:
if (e->e_xfp != NULL)
(void) fflush(e->e_xfp);
#if !HASFLOCK
/*
** Since fcntl locking has the interesting semantic that
** the lock is owned by a process, not by an open file
** descriptor, we have to flush this to the queue, and
** then restart from scratch in the child.
*/
{
/* save id for future use */
char *qid = e->e_id;
/* now drop the envelope in the parent */
e->e_flags |= EF_INQUEUE;
dropenvelope(e, splitenv != NULL);
/* arrange to reacquire lock after fork */
e->e_id = qid;
}
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
/* save id for future use */
char *qid = ee->e_id;
/* drop envelope in parent */
ee->e_flags |= EF_INQUEUE;
dropenvelope(ee, FALSE);
/* and save qid for reacquisition */
ee->e_id = qid;
}
#endif /* !HASFLOCK */
/*
** 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)
{
syserr("deliver: fork 1");
#if HASFLOCK
goto queueonly;
#else /* HASFLOCK */
e->e_id = NULL;
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
ee->e_id = NULL;
return;
#endif /* HASFLOCK */
}
else if (pid > 0)
{
#if HASFLOCK
/* be sure we leave the temp files to our child */
/* close any random open files in the envelope */
closexscript(e);
if (e->e_dfp != NULL)
(void) bfclose(e->e_dfp);
e->e_dfp = NULL;
e->e_flags &= ~EF_HAS_DF;
/* can't call unlockqueue to avoid unlink of xfp */
if (e->e_lockfp != NULL)
(void) fclose(e->e_lockfp);
else
syserr("%s: sendall: null lockfp", e->e_id);
e->e_lockfp = NULL;
#endif /* HASFLOCK */
/* make sure the parent doesn't own the envelope */
e->e_id = NULL;
/* catch intermediate zombie */
(void) waitfor(pid);
return;
}
/*
** Since we have accepted responsbility for the message,
** change the SIGTERM handler. intsig() (the old handler)
** would remove the envelope if this was a command line
** message submission.
*/
(void) setsignal(SIGTERM, SIG_DFL);
/* double fork to avoid zombies */
pid = fork();
if (pid > 0)
exit(EX_OK);
save_errno = errno;
/* be sure we are immune from the terminal */
disconnect(2, e);
clearstats();
/* prevent parent from waiting if there was an error */
if (pid < 0)
{
errno = save_errno;
syserr("deliver: fork 2");
#if HASFLOCK
e->e_flags |= EF_INQUEUE;
#else /* HASFLOCK */
e->e_id = NULL;
#endif /* HASFLOCK */
finis(TRUE, ExitStat);
}
/* be sure to give error messages in child */
QuickAbort = FALSE;
/*
** Close any cached connections.
**
** We don't send the QUIT protocol because the parent
** still knows about the connection.
**
** This should only happen when delivering an error
** message.
*/
mci_flush(FALSE, NULL);
#if HASFLOCK
break;
#else /* HASFLOCK */
/*
** Now reacquire and run the various queue files.
*/
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
ENVELOPE *sibling = ee->e_sibling;
(void) dowork(ee->e_queuedir, ee->e_id,
FALSE, FALSE, ee);
ee->e_sibling = sibling;
}
(void) dowork(e->e_queuedir, e->e_id,
FALSE, FALSE, e);
finis(TRUE, ExitStat);
#endif /* HASFLOCK */
}
sendenvelope(e, mode);
dropenvelope(e, TRUE);
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
CurEnv = ee;
if (mode != SM_VERIFY)
openxscript(ee);
sendenvelope(ee, mode);
dropenvelope(ee, TRUE);
}
CurEnv = e;
Verbose = oldverbose;
if (mode == SM_FORK)
finis(TRUE, ExitStat);
}
static void
sendenvelope(e, mode)
register ENVELOPE *e;
int mode;
{
register ADDRESS *q;
bool didany;
if (tTd(13, 10))
dprintf("sendenvelope(%s) e_flags=0x%lx\n",
e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
e->e_flags);
if (LogLevel > 80)
sm_syslog(LOG_DEBUG, e->e_id,
"sendenvelope, flags=0x%lx",
e->e_flags);
/*
** If we have had global, fatal errors, don't bother sending
** the message at all if we are in SMTP mode. Local errors
** (e.g., a single address failing) will still cause the other
** addresses to be sent.
*/
if (bitset(EF_FATALERRS, e->e_flags) &&
(OpMode == MD_SMTP || OpMode == MD_DAEMON))
{
e->e_flags |= EF_CLRQUEUE;
return;
}
/* Don't attempt deliveries if we want to bounce now */
if (!bitset(EF_RESPONSE, e->e_flags) &&
TimeOuts.to_q_return[e->e_timeoutclass] == NOW)
return;
/*
** Run through the list and send everything.
**
** Set EF_GLOBALERRS so that error messages during delivery
** result in returned mail.
*/
e->e_nsent = 0;
e->e_flags |= EF_GLOBALERRS;
define(macid("{envid}", NULL), e->e_envid, e);
define(macid("{bodytype}", NULL), e->e_bodytype, e);
didany = FALSE;
/* now run through the queue */
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
#if XDEBUG
char wbuf[MAXNAME + 20];
(void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
MAXNAME, q->q_paddr);
checkfd012(wbuf);
#endif /* XDEBUG */
if (mode == SM_VERIFY)
{
e->e_to = q->q_paddr;
if (QS_IS_SENDABLE(q->q_state))
{
if (q->q_host != NULL && q->q_host[0] != '\0')
message("deliverable: mailer %s, host %s, user %s",
q->q_mailer->m_name,
q->q_host,
q->q_user);
else
message("deliverable: mailer %s, user %s",
q->q_mailer->m_name,
q->q_user);
}
}
else if (QS_IS_OK(q->q_state))
{
#if QUEUE
/*
** Checkpoint the send list every few addresses
*/
if (CheckpointInterval > 0 &&
e->e_nsent >= CheckpointInterval)
{
queueup(e, FALSE);
e->e_nsent = 0;
}
#endif /* QUEUE */
(void) deliver(e, q);
didany = TRUE;
}
}
if (didany)
{
e->e_dtime = curtime();
e->e_ntries++;
}
#if XDEBUG
checkfd012("end of sendenvelope");
#endif /* XDEBUG */
}
/*
** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
**
** Parameters:
** e -- the existing envelope
** ee -- the new envelope
** type -- the queue file type (e.g., 'd')
**
** Returns:
** none
*/
static void
dup_queue_file(e, ee, type)
struct envelope *e, *ee;
int type;
{
char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
ee->e_dfp = NULL;
ee->e_xfp = NULL;
/*
** Make sure both are in the same directory.
*/
snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type));
snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type));
if (link(f1buf, f2buf) < 0)
{
int save_errno = errno;
syserr("sendall: link(%s, %s)", f1buf, f2buf);
if (save_errno == EEXIST)
{
if (unlink(f2buf) < 0)
{
syserr("!sendall: unlink(%s): permanent",
f2buf);
/* NOTREACHED */
}
if (link(f1buf, f2buf) < 0)
{
syserr("!sendall: link(%s, %s): permanent",
f1buf, f2buf);
/* NOTREACHED */
}
}
}
}
/*
** DOFORK -- do a fork, retrying a couple of times on failure.
**
** This MUST be a macro, since after a vfork we are running
** two processes on the same stack!!!
**
** Parameters:
** none.
**
** Returns:
** From a macro??? You've got to be kidding!
**
** Side Effects:
** Modifies the ==> LOCAL <== variable 'pid', leaving:
** pid of child in parent, zero in child.
** -1 on unrecoverable error.
**
** Notes:
** I'm awfully sorry this looks so awful. That's
** vfork for you.....
*/
#define NFORKTRIES 5
#ifndef FORK
# define FORK fork
#endif /* ! FORK */
#define DOFORK(fORKfN) \
{\
register int i;\
\
for (i = NFORKTRIES; --i >= 0; )\
{\
pid = fORKfN();\
if (pid >= 0)\
break;\
if (i > 0)\
(void) sleep((unsigned) NFORKTRIES - i);\
}\
}
/*
** DOFORK -- simple fork interface to DOFORK.
**
** Parameters:
** none.
**
** Returns:
** pid of child in parent.
** zero in child.
** -1 on error.
**
** Side Effects:
** returns twice, once in parent and once in child.
*/
int
dofork()
{
register pid_t pid = -1;
DOFORK(fork);
return pid;
}
/*
** DELIVER -- Deliver a message to a list of addresses.
**
** This routine delivers to everyone on the same host as the
** user on the head of the list. It is clever about mailers
** that don't handle multiple users. It is NOT guaranteed
** that it will deliver to all these addresses however -- so
** deliver should be called once for each address on the
** list.
**
** Parameters:
** e -- the envelope to deliver.
** firstto -- head of the address list to deliver to.
**
** Returns:
** zero -- successfully delivered.
** else -- some failure, see ExitStat for more info.
**
** Side Effects:
** The standard input is passed off to someone.
*/
#ifndef NO_UID
# define NO_UID -1
#endif /* ! NO_UID */
#ifndef NO_GID
# define NO_GID -1
#endif /* ! NO_GID */
static int
deliver(e, firstto)
register ENVELOPE *e;
ADDRESS *firstto;
{
char *host; /* host being sent to */
char *user; /* user being sent to */
char **pvp;
register char **mvp;
register char *p;
register MAILER *m; /* mailer for this recipient */
ADDRESS *volatile ctladdr;
ADDRESS *volatile contextaddr = NULL;
register MCI *volatile mci;
register ADDRESS *to = firstto;
volatile bool clever = FALSE; /* running user smtp to this mailer */
ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
int rcode; /* response code */
int lmtp_rcode = EX_OK;
int nummxhosts = 0; /* number of MX hosts available */
int hostnum = 0; /* current MX host index */
char *firstsig; /* signature of firstto */
pid_t pid = -1;
char *volatile curhost;
register u_short port = 0;
#if NETUNIX
char *mux_path = NULL; /* path to UNIX domain socket */
#endif /* NETUNIX */
time_t xstart;
bool suidwarn;
bool anyok; /* at least one address was OK */
bool goodmxfound = FALSE; /* at least one MX was OK */
bool ovr;
#if _FFR_DYNAMIC_TOBUF
int strsize;
int rcptcount;
static int tobufsize = 0;
static char *tobuf = NULL;
#else /* _FFR_DYNAMIC_TOBUF */
char tobuf[TOBUFSIZE]; /* text line of to people */
#endif /* _FFR_DYNAMIC_TOBUF */
int mpvect[2];
int rpvect[2];
char *mxhosts[MAXMXHOSTS + 1];
char *pv[MAXPV + 1];
char buf[MAXNAME + 1];
char rpathbuf[MAXNAME + 1]; /* translated return path */
errno = 0;
if (!QS_IS_OK(to->q_state))
return 0;
suidwarn = geteuid() == 0;
m = to->q_mailer;
host = to->q_host;
CurEnv = e; /* just in case */
e->e_statmsg = NULL;
#if SMTP
SmtpError[0] = '\0';
#endif /* SMTP */
xstart = curtime();
if (tTd(10, 1))
dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
e->e_id, m->m_name, host, to->q_user);
if (tTd(10, 100))
printopenfds(FALSE);
/*
** Clear $&{client_*} macros if this is a bounce message to
** prevent rejection by check_compat ruleset.
*/
if (bitset(EF_RESPONSE, e->e_flags))
{
define(macid("{client_name}", NULL), "", e);
define(macid("{client_addr}", NULL), "", e);
define(macid("{client_port}", NULL), "", e);
}
/*
** Do initial argv setup.
** Insert the mailer name. Notice that $x expansion is
** NOT done on the mailer name. Then, if the mailer has
** a picky -f flag, we insert it as appropriate. This
** code does not check for 'pv' overflow; this places a
** manifest lower limit of 4 for MAXPV.
** The from address rewrite is expected to make
** the address relative to the other end.
*/
/* rewrite from address, using rewriting rules */
rcode = EX_OK;
if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
p = e->e_sender;
else
p = e->e_from.q_paddr;
p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
if (strlen(p) >= (SIZE_T) sizeof rpathbuf)
{
p = shortenstring(p, MAXSHORTSTR);
syserr("remotename: huge return %s", p);
}
snprintf(rpathbuf, sizeof rpathbuf, "%s", p);
define('g', rpathbuf, e); /* translated return path */
define('h', host, e); /* to host */
Errors = 0;
pvp = pv;
*pvp++ = m->m_argv[0];
/* insert -f or -r flag as appropriate */
if (FromFlag &&
(bitnset(M_FOPT, m->m_flags) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -