/*
* Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
* 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 "sm/generic.h"
SM_RCSID("@(#)$Id: updrcpt.c,v 1.96 2006/12/11 01:22:10 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "qmgr.h"
#include "qm_throttle.h"
#include "sm/edb.h"
#include "sm/edbc.h"
#include "sm/aqrdq.h"
#include "log.h"
/*
** QDA_UPD_DSN -- Remove failed recipients (from DEFEDB) by adding them to
** the EDB request list after a delivery attempt for their bounce message
** has been made.
**
** Parameters:
** qmgr_ctx -- QMGR context
** aq_ta -- AQ transaction
** aq_rcpt -- AQ bounce recipient
** ss_ta_id -- SMTPS transaction id
** edb_req_hd -- head of request list for (DEF)EDB
**
** Returns:
** usual sm_error code; ENOMEM,
**
** Side Effects:
** if ok: decrease aqt_rcpts_perm, aqt_rcpts_left
** aqr_dsn_rcpts = 0, free aqr_dsns
** on error: some entries may have been appended to edb_req_hd,
** it's up to the caller to get rid of them
** (maybe undo this locally when requested??)
**
** Called by: q_upd_rcpt_ok(), q_upd_rcpt_fail()
**
** Last code review:
** Last code change:
*/
static sm_ret_T
qda_upd_dsn(qmgr_ctx_P qmgr_ctx, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt, sessta_id_T ss_ta_id, edb_req_hd_P edb_req_hd)
{
sm_ret_T ret;
uint idx;
rcpt_idx_T rcpt_idx_dsn;
rcpt_id_T rcpt_id_dsn;
QM_LEV_DPRINTFC(QDC_UPDRCPT, 4, (QM_DEBFP, "sev=DBG, func=qda_upd_dsn, aq_ta=%p, aq_rcpt=%p, idx=%u, n=%d\n", aq_ta, aq_rcpt, aq_rcpt->aqr_idx, aq_rcpt->aqr_dsn_rcpts));
ret = SM_SUCCESS;
SM_ASSERT(aq_rcpt->aqr_dsn_rcpts_max >= aq_rcpt->aqr_dsn_rcpts);
for (idx = 0; idx < aq_rcpt->aqr_dsn_rcpts; idx++)
{
/* remove rcpt from persistent DB */
rcpt_idx_dsn = aq_rcpt->aqr_dsns[idx];
/*
** Is the recipient in DEFEDB?
** q_upd_rcpt_fail() seems to guarantee this.
** Note: this is "hard" to check: it would require a DEFEDB
** access which is just not worth it.
*/
sm_snprintf(rcpt_id_dsn, sizeof(rcpt_id_dsn),
SMTP_RCPTID_FORMAT, ss_ta_id, rcpt_idx_dsn);
#if QMGR_TEST
/* trigger an error if requested (add some condition??) */
if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_UPD_DSN))
ret = sm_error_temp(SM_EM_EDB, ENOMEM);
else /* WARNING: be careful about changing the next statement */
#endif /* QMGR_TEST */
ret = edb_rcpt_rm_req(qmgr_ctx->qmgr_edb, rcpt_id_dsn, edb_req_hd);
if (sm_is_err(ret))
{
/*
** XXX How to handle this error?
** Set a QMGR status flag?
** Set a flag in the recipient that should have been
** removed? The rcpt is (most likely) not in AQ
** but only in DEFEDB.
**
** Bail out for now. Caller must handle the rest,
** currently: stop (due to resource problem).
** Note: this isn't really fatal, a better solution
** can be implemented later on.
*/
QM_LEV_DPRINTFC(QDC_UPDRCPT, 0, (QM_DEBFP, "sev=ERROR, func=qda_upd_dsn, ss_ta=%s, rcpt_idx_dsn=%u, edb_rcpt_rm_req=%#x\n", ss_ta_id, rcpt_idx_dsn, ret));
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=qda_upd_dsn, ss_ta=%s, rcpt_idx_dsn=%d, edb_rcpt_rm_req=%#x",
ss_ta_id, rcpt_idx_dsn, ret);
goto error;
}
else
{
sm_ret_T res;
AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_R);
res = edbc_rmentry(qmgr_ctx->qmgr_edbc, rcpt_id_dsn);
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=qda_upd_dsn, ss_ta=%s, idx=%u, status=remove_bounce, edb_rcpt_rm_req=%#x, edbc_rmentry=%r\n", ss_ta_id, rcpt_idx_dsn, ret, res));
}
}
/*
** Decrement number of recipients that permanently failed.
** This is ok even if it's "just" a timeout because
** that caused an increment of aqt_rcpts_perm too
** (see q_upd_rcpt_fail()).
*/
idx = aq_rcpt->aqr_dsn_rcpts;
if (aq_ta->aqt_rcpts_perm >= idx)
{
aq_ta->aqt_rcpts_perm -= idx;
QM_LEV_DPRINTFC(QDC_UPDRCPT, 4, (QM_DEBFP, "sev=DBG, func=qda_upd_dsn, aq_ta=%p, aqt_rcpts_perm=%d\n", aq_ta, aq_ta->aqt_rcpts_perm));
}
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=qda_upd_dsn, status=counter_error, ta=%s, aqt_rcpts_perm=%u, aqr_dsn_rcpts=%u",
aq_ta->aqt_ss_ta_id, aq_ta->aqt_rcpts_perm, idx);
}
/*
** If multiple recipients have been delivered in a single DSN,
** then we need to decrease the number of recipients left accordingly.
** If the bounce was successful delivered, then
** aqt_rcpts_left has been decreased by 1 already.
** If the bounce was not successful delivered, then it will
** be added to defedb, hence there will be one more rcpt.
** Therefore we have to decrease aqt_rcpts_left by the number of
** additional recipients (aqr_dsn_rcpts - 1).
*/
/* SM_ASSERT(idx == aq_rcpt->aqr_dsn_rcpts); */
if (idx > 1)
{
--idx;
if (aq_ta->aqt_rcpts_left >= idx)
{
QM_LEV_DPRINTFC(QDC_UPDRCPT, 4, (QM_DEBFP, "sev=DBG, func=qda_upd_dsn, ta=%s, idx=%u, aqr_flags=%#x, dsn_rcpts=%u, aqt_rcpts_left=%u, decrease=%u\n",
aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx,
aq_rcpt->aqr_flags, aq_rcpt->aqr_dsn_rcpts,
aq_ta->aqt_rcpts_left, idx));
aq_ta->aqt_rcpts_left -= idx;
AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
}
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=qda_upd_dsn, ta=%s, idx=%u, aqr_flags=%#x, dsn_rcpts=%u, aqt_rcpts_left=%u, decrease=%u, when=before_decreasing",
aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx,
aq_rcpt->aqr_flags, aq_rcpt->aqr_dsn_rcpts,
aq_ta->aqt_rcpts_left, idx);
/* SM_ASSERT(aq_ta->aqt_rcpts_left >= idx); ? */
}
}
aq_rcpt->aqr_dsn_rcpts = 0;
if (aq_rcpt->aqr_dsns != NULL)
{
size_t size_dsns;
SM_ASSERT(aq_rcpt->aqr_dsn_rcpts_max > 0);
size_dsns = sizeof(*(aq_rcpt->aqr_dsns)) *
aq_rcpt->aqr_dsn_rcpts_max;
SM_ASSERT(aq_rcpt->aqr_dsn_rcpts_max <= size_dsns);
SM_FREE_SIZE(aq_rcpt->aqr_dsns, size_dsns);
aq_rcpt->aqr_dsn_rcpts_max = 0;
}
error:
return ret;
}
/*
** Q_RCPT_CHK_TMOUT -- Check whether recipient is too long in queue
**
** Parameters:
** qmgr_ctx -- QMGR context
** ss_ta_id -- SMTPS transaction id
** aq_ta -- AQ transaction
** aq_rcpt -- AQ recipient
** time_now -- current time
**
** Returns:
** SM_SUCCESS
**
** Side Effects: may change status of recipient and ta counters
** aqt_rcpts_temp, aqt_rcpts_perm
**
** Called by: q_upd_rcpt_fail()
**
** Last code review:
** Last code change:
*/
static sm_ret_T
q_rcpt_chk_tmout(qmgr_ctx_P qmgr_ctx, sessta_id_T ss_ta_id, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt, time_T time_now)
{
#if SM_DELAYED_DSN
/* XXX defaults to on for now */
if (!AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG|AQR_DSNFL_D_HBG) &&
/* AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_REQ) && */
!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC) &&
aq_rcpt->aqr_st_time + qmgr_ctx->qmgr_cnf.q_cnf_tmo_delay
< time_now)
{
AQR_SET_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG);
}
#endif
/*
** Too long in queue and can't be retried? Then count it
** as permanent failure (and mark it accordingly)
*/
if (aq_rcpt->aqr_st_time + qmgr_ctx->qmgr_cnf.q_cnf_tmo_return
< time_now &&
(!AQR_MORE_DESTS(aq_rcpt) || AQR_DEFER(aq_rcpt)))
{
/* SM_ASSERT(!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC)); */
/* Should this really set AQR_FL_PERM?? */
AQR_SET_FLAG(aq_rcpt, AQR_FL_PERM|AQR_FL_DSN_TMT);
/* SM_ASSERT(aq_ta->aqt_rcpts_temp > 0); */
if (aq_ta->aqt_rcpts_temp > 0)
--aq_ta->aqt_rcpts_temp;
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=q_rcpt_chk_tmout, status=timeout, ss_ta=%s, idx=%u, aqt_rcpts_temp=0, when=before_decreasing"
, ss_ta_id, aq_rcpt->aqr_idx);
QM_LEV_DPRINTFC(QDC_UPDRCPT, 1, (QM_DEBFP, "sev=DBG, func=q_rcpt_chk_tmout, aq_ta=%p, perm=%u, left=%u\n", aq_ta, aq_ta->aqt_rcpts_perm , aq_ta->aqt_rcpts_left));
/* abort */
SM_ASSERT(aq_ta->aqt_rcpts_temp > 0);
}
SM_ASSERT(aq_ta->aqt_rcpts_perm < UINT_MAX);
++aq_ta->aqt_rcpts_perm;
QM_LEV_DPRINTFC(QDC_UPDRCPT, 1, (QM_DEBFP, "sev=DBG, func=q_rcpt_chk_tmout, status=TO, aq_rcpt=%p, rcpt=%@S, now-aqr_st_time=%ld, aqr_flags=%#x\n", aq_rcpt, aq_rcpt->aqr_pa, (long) (time_now - aq_rcpt->aqr_st_time), aq_rcpt->aqr_flags));
}
return SM_SUCCESS;
}
/*
** Q_STORE_DDSN -- Store a delayed DSN recipient in DEFEDB etc
**
** Parameters:
** qmgr_ctx -- QMGR context
** ss_ta_id -- SMTPS transaction id
** aq_ta -- AQ transaction
** aq_rcpt -- AQ recipient (which is delayed)
** aq_rcpt_dsn -- AQ recipient which contains the DSN
** edb_req_hd -- head of request list for (DEF)EDB
** pdelay_next_try -- (pointer to) delay until next try (output)
**
** Returns:
** success: flags as shown in sm/qmgr-int.h
** to activate scheduler or SMAR.
** Alternatively flags might be an output parameter.
** error: usual sm_error code
**
** Side Effects:
**
** Called by:
**
** Locking: aq_ctx and edbc must be locked.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
q_store_ddsn(qmgr_ctx_P qmgr_ctx, sessta_id_T ss_ta_id, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt, aq_rcpt_P aq_rcpt_dsn, edb_req_hd_P edb_req_hd, time_T time_now, int *pdelay_next_try)
{
sm_ret_T ret;
int delay;
/* treat this as temporary error */
aq_rcpt_dsn->aqr_status = SMTP_TMO_DDSN;
/* XXX "timeout" */
delay = 2;
aq_rcpt_dsn->aqr_next_try = time_now + delay;
if (delay < *pdelay_next_try || *pdelay_next_try == 0)
*pdelay_next_try = delay;
#if QMGR_TEST
/* trigger an error if requested (add some condition??) */
if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_DDSN))
{
/* only once */
SM_CLR_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_DDSN);
ret = sm_error_temp(SM_EM_EDB, ENOMEM);
}
else /* WARNING: be careful about changing the next statement */
#endif /* QMGR_TEST */
ret = edb_rcpt_app(qmgr_ctx->qmgr_edb, aq_rcpt_dsn, edb_req_hd, aq_rcpt_dsn->aqr_status);
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=q_store_ddsn, aq_rcpt_dsn=%p, ss_ta=%s, idx=%u, edb_rcpt_app=%r\n", aq_rcpt, ss_ta_id, aq_rcpt_dsn->aqr_idx, ret));
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=q_store_ddsn, aq_rcpt_dsn=%p, ss_ta=%s, edb_rcpt_app=%m",
aq_rcpt_dsn, ss_ta_id, ret);
/* let's hope it works the next time */
AQR_CLR_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG);
SM_ASSERT(aq_ta->aqt_rcpts_left > 0);
SM_ASSERT(aq_ta->aqt_rcpts_tot > 0);
--aq_ta->aqt_rcpts_left;
--aq_ta->aqt_rcpts_tot;
}
else
{
rcpt_id_T rcpt_dsn_id;
++aq_ta->aqt_rcpts_temp;
sm_snprintf(rcpt_dsn_id, sizeof(rcpt_dsn_id),
SMTP_RCPTID_FORMAT, ss_ta_id, aq_rcpt_dsn->aqr_idx);
AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_R|AQ_TA_FL_EDB_UPD_C);
ret = edbc_add(qmgr_ctx->qmgr_edbc, rcpt_dsn_id, aq_rcpt_dsn->aqr_next_try, false);
if (sm_is_err(ret))
{
QMGR_SET_SFLAG(qmgr_ctx, QMGR_SFL_EDBC);
if (sm_error_value(ret) == ENOMEM)
QMGR_SET_RFLAG(qmgr_ctx, QMGR_RFL_MEM);
else if (ret == sm_error_temp(SM_EM_Q_EDBC, SM_E_FULL))
QMGR_SET_RFLAG(qmgr_ctx, QMGR_RFL_EBDC);
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 3,
"sev=ERROR, func=q_store_ddsn, rcpt_id=%s, next_try=%6ld, edbc_add=%m, entries=%u",
rcpt_dsn_id, (long) aq_rcpt_dsn->aqr_next_try,
ret, qmgr_ctx->qmgr_edbc->edbc_entries);
}
}
ret = aq_rcpt_rm(qmgr_ctx->qmgr_aq, aq_rcpt_dsn, AQR_RM_I_RDQ|AQR_RM_I_WAITQ);
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=q_store_ddsn, aq_rcpt_dsn=%p, ss_ta=%s, aq_rcpt_rm=%m",
aq_rcpt_dsn, ss_ta_id, ret);
}
return SM_SUCCESS;
}
/*
** Q_UPD_RCPT_FAIL -- Update status for one failed recipient
**
** This function may append entries to the update queues for
** DEFEDB and IBDB which need to be taken care of by the caller.
**
** Parameters:
** qmgr_ctx -- QMGR context
** ss_ta_id -- SMTPS transaction id
** status -- status of (entire) transaction
** aq_ta -- AQ transaction
** aq_rcpt -- AQ recipient
** edb_req_hd -- head of request list for (DEF)EDB
** ibdb_req_hd -- head of request list for IBDB
** pdelay_next_try -- (pointer to) delay until next try (output)
** errmsg -- error message (might be NULL)
** (must be "sanitized" by caller)
**
** Returns:
** success: flags as shown in sm/qmgr-int.h
** to activate scheduler or SMAR.
** Alternatively flags might be an output parameter.
** error: usual sm_error code
**
** Side Effects:
** on error: might have changed edb request list (qda_upd_dsn),
** ibdb request list.
** may change aqt_rcpts_temp, aqt_rcpts_perm.
** can change several aq_rcpt fields.
**
** Called by: q_upd_rcpt_stat()
**
** Locking: aq_ctx and edbc must be locked.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
q_upd_rcpt_fail(qmgr_ctx_P qmgr_ctx, sessta_id_T ss_ta_id,
sm_ret_T rcpt_status, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt,
ibdb_rcpt_P ibdb_rcpt, rcpt_id_T rcpt_id,
edb_req_hd_P edb_req_hd, ibdb_req_hd_P ibdb_req_hd, sm_str_P errmsg,
int *pdelay_next_try)
{
sm_ret_T ret, flags, rv;
time_T time_now;
SM_IS_QMGR_CTX(qmgr_ctx);
SM_IS_AQ_TA(aq_ta);
SM_IS_AQ_RCPT(aq_rcpt);
SM_REQUIRE(pdelay_next_try != NULL);
rv = ret = SM_SUCCESS;
flags = 0;
time_now = evthr_time(qmgr_ctx->qmgr_ev_ctx);
/* SMAR failures will cause this to be NULL */
if (aq_rcpt->aqr_addrs != NULL &&
aq_rcpt->aqr_addr_cur < aq_rcpt->aqr_addr_max)
{
QM_LEV_DPRINTFC(QDC_UPDRCPT, 6, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_fail, aq_rcpt=%p, addrs!=NULL, cur=%u, max=%u\n", aq_rcpt, aq_rcpt->aqr_addr_cur, aq_rcpt->aqr_addr_max));
aq_rcpt->aqr_addr_fail = aq_rcpt->aqr_addrs[aq_rcpt->aqr_addr_cur].aqra_ipv4;
}
/* Unconditionally set status */
aq_rcpt->aqr_status = rcpt_status;
/* Do something more with this?? */
switch (smtp_reply_type(rcpt_status))
{
default:
/* XXX What to do in this case?? abort? */
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=q_upd_rcpt_fail, ss_ta=%s, idx=%u, stat=%d, problem=status_is_unknown"
, ss_ta_id, aq_rcpt->aqr_idx, rcpt_status);
/* XXX set status to a temporary error for now; abort?? */
aq_rcpt->aqr_status = rcpt_status = SMTPC_TEMP_ST;
/* FALLTHROUGH */
case SMTP_RTYPE_TEMP:
AQR_SET_FLAG(aq_rcpt, AQR_FL_TEMP);
(void) q_rcpt_chk_tmout(qmgr_ctx, ss_ta_id, aq_ta, aq_rcpt, time_now);
break;
case SMTP_RTYPE_PERM:
/* Always set AQR_FL_DSN_PERM?? */
AQR_SET_FLAG(aq_rcpt, AQR_FL_PERM|AQR_FL_DSN_PERM);
break;
}
/* note: AQR_FL_DSN_TMT currently implies AQR_FL_PERM */
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC) &&
(AQR_IS_FLAG(aq_rcpt, AQR_FL_PERM|AQR_FL_DSN_TMT) ||
(AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG) &&
!AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG))))
{
bool isddsn;
aq_rcpt_P aq_rcpt_dsn;
isddsn = AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG) &&
!AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG);
/* Need to generate DSN */
ret = qm_bounce_add(qmgr_ctx, aq_ta, aq_rcpt, errmsg, &aq_rcpt_dsn);
if (isddsn && sm_is_success(ret))
(void) q_store_ddsn(qmgr_ctx, ss_ta_id, aq_ta, aq_rcpt, aq_rcpt_dsn, edb_req_hd, time_now, pdelay_next_try);
if (sm_is_success(ret))
flags |= ret;
else if (sm_error_value(ret) == ENOMEM)
{
/* throttle servers; do this in caller?? fixme */
(void) qm_control(qmgr_ctx, 1, 100, QMGR_RFL_MEM_I, THR_NO_LOCK);
}
/* note: error is handled below by putting rcpt into EDB */
}
/*
** This needs to be done only if there are no more chances
** to deliver the mail in this attempt (i.e., all destination hosts
** have been tried) or some other error occurred, e.g.,
** timeout in scheduler or failure in SMAR,
** or if a DSN has been generated.
** Note: the latter case is not really necessary but a result of
** the current bounce handling implementation, e.g.,
** qda_upd_dsn() requires that the recipients are in DEFEDB.
**
** XXX Problem: a bounce isn't written to an external DB when it
** is generated, hence the data in DEFEDB is inconsistent at
** some point... does the recovery program deal with that?
*/
if (!AQR_MORE_DESTS(aq_rcpt) || AQR_DEFER(aq_rcpt) ||
(AQR_IS_FLAG(aq_rcpt, AQR_FL_PERM|AQR_FL_DSN_TMT) &&
!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC)))
{
aq_rcpt->aqr_tries++;
/*
** Need to try this again for
** - temporary failure
** - permanent failure but no bounce (because generation
** of bounce failed, see above).
*/
if (smtp_is_reply_temp(aq_rcpt->aqr_status) ||
(smtp_is_reply_fail(aq_rcpt->aqr_status) &&
!aq_rcpt_has_bounce(aq_rcpt)))
{
uint d;
d = qm_delay_next_try(qmgr_ctx, aq_rcpt);
aq_rcpt->aqr_next_try = time_now + d;
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INFO, 14,
"sev=INFO, func=q_upd_rcpt_fail, ss_ta=%s, idx=%u, qm_delay_next_try=%d, *pdelay_next_try=%d"
, ss_ta_id, aq_rcpt->aqr_idx
, d, *pdelay_next_try);
if (d < *pdelay_next_try || *pdelay_next_try == 0)
*pdelay_next_try = d;
}
ret = edb_rcpt_app(qmgr_ctx->qmgr_edb, aq_rcpt, edb_req_hd, rcpt_status);
if (sm_is_err(ret))
{
/*
** fixme: How to handle this error?
** Set a QMGR status flag?
** Remember that the entry must not be removed
** from IBDB (if it was there) by setting a flag
** in aq_rcpt?
** For now: bail out and let caller deal with it.
*/
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=q_upd_rcpt_fail, aq_rcpt=%p, ss_ta=%s, idx=%u, edb_rcpt_app=%m",
aq_rcpt, ss_ta_id, aq_rcpt->aqr_idx, ret);
rv = ret; /* XXX overwrite? */
goto error;
}
else
{
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_fail, aq_rcpt=%p, ss_ta=%s, idx=%u, edb_rcpt_app=%r\n", aq_rcpt, ss_ta_id, aq_rcpt->aqr_idx, ret));
AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_R);
}
if (sm_is_success(ret) &&
AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC|AQR_FL_IS_DBNC))
{
ret = qda_upd_dsn(qmgr_ctx, aq_ta, aq_rcpt, ss_ta_id, edb_req_hd);
if (sm_is_err(ret))
{
rv = ret;
goto error;
}
}
/* Append to EDB cache if necessary. */
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_PERM|AQR_FL_DSN_TMT))
{
ret = edbc_add(qmgr_ctx->qmgr_edbc, rcpt_id, aq_rcpt->aqr_next_try, false);
if (sm_is_err(ret))
{
QMGR_SET_SFLAG(qmgr_ctx, QMGR_SFL_EDBC);
if (sm_error_value(ret) == ENOMEM)
QMGR_SET_RFLAG(qmgr_ctx, QMGR_RFL_MEM);
else if (ret == sm_error_temp(SM_EM_Q_EDBC, SM_E_FULL))
QMGR_SET_RFLAG(qmgr_ctx, QMGR_RFL_EBDC);
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 3,
"sev=ERROR, func=q_upd_rcpt_fail, rcpt_id=%s, next_try=%6ld, edbc_add=%m, entries=%u",
rcpt_id, (long)aq_rcpt->aqr_next_try,
ret, qmgr_ctx->qmgr_edbc->edbc_entries);
}
else
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_fail, rcpt_id=%s, next_try=%6ld, edbc_add=%r, entries=%u\n", rcpt_id, (long) aq_rcpt->aqr_next_try, ret, qmgr_ctx->qmgr_edbc->edbc_entries));
}
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IQDB))
{
ret = ibdb_rcpt_app(qmgr_ctx->qmgr_ibdb, ibdb_rcpt, ibdb_req_hd,
smtp_is_reply_temp(aq_rcpt->aqr_status)
? IBDB_RCPT_TEMP : IBDB_RCPT_PERM);
if (sm_is_err(ret))
{
/*
** XXX How to handle this error?
** Set a QMGR status flag?
** The system is probably out of memory.
** Caller should deal with it?
*/
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=q_upd_rcpt_fail, ss_ta=%s, ibdb_rcpt_app=%m",
ss_ta_id, ret);
rv = ret;
goto error;
}
else
QM_LEV_DPRINTFC(QDC_UPDRCPT, 2, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_fail, ss_ta=%s, ibdb_rcpt_app=%r\n", ss_ta_id, ret));
}
}
#if 0
else
{
/*
** Let's try again... changes done below
** otherwise the test whether a retry is
** possible fails (or at least must be changed).
*/
}
#endif /* 0 */
error:
return sm_is_err(rv) ? rv : flags;
}
/*
** Q_UPD_RCPT_OK -- Update status for one delivered recipient
** (or a double bounce)
**
** Parameters:
** qmgr_ctx -- QMGR context
** ss_ta_id -- SMTPS transaction id
** status -- status of (entire) transaction
** aq_ta -- AQ transaction
** aq_rcpt -- AQ recipient
** ibdb_rcpt -- IBDB recipient
** rcpt_id -- recipient id
** edb_req_hd -- head of request list for (DEF)EDB
** ibdb_req_hd -- head of request list for IBDB
**
** Returns:
** usual sm_error code; ENOMEM,
** any error is "directly" returned to the caller, no cleanup
** is performed.
**
** Side Effects:
** on error: might have changed edb request list (qda_upd_dsn)
** may write to ibdb (directly, not via request)
** may decrease aqt_rcpts_left.
**
** Called by: q_upd_rcpt_stat()
**
** Locking: aq_ctx and edbc must be locked.
**
** Note: DSNs are not implemented! (in case of a SUCCESS DSN
** we would need to store that information
** somewhere, e.g., DEFEDB).
**
** Last code review: 2005-03-01 23:58:05
** Last code change: 2006-04-04 19:27:41
*/
static sm_ret_T
q_upd_rcpt_ok(qmgr_ctx_P qmgr_ctx, sessta_id_T ss_ta_id,
sm_ret_T rcpt_status, aq_ta_P aq_ta,
aq_rcpt_P aq_rcpt, ibdb_rcpt_P ibdb_rcpt, rcpt_id_T rcpt_id,
edb_req_hd_P edb_req_hd, ibdb_req_hd_P ibdb_req_hd)
{
sm_ret_T ret;
SM_IS_QMGR_CTX(qmgr_ctx);
SM_IS_AQ_TA(aq_ta);
SM_IS_AQ_RCPT(aq_rcpt);
ret = SM_SUCCESS;
/* is this a double bounce that could not be delivered? */
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC) && rcpt_status != SM_SUCCESS)
{
/* drop it on the floor... one less rcpt left */
if (aq_ta->aqt_rcpts_left > 0)
--aq_ta->aqt_rcpts_left;
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=q_upd_rcpt_ok, aq_ta=%p, aq_rcpt=%p, aqr_flags=%#x, bounce=doublebounce, aqt_rcpts_left=0, when=before_decreasing",
aq_ta, aq_rcpt, aq_rcpt->aqr_flags);
/* SM_ASSERT(aq_ta->aqt_rcpts_left > 0); ? */
}
/* can't do anything about this... just log it */
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_DEBUG, 10,
"sev=DBG, func=q_upd_rcpt_ok, aq_ta=%p, aq_rcpt=%p, aqr_flags=%#x, bounce=doublebounce, aqt_rcpts_left=%d, status=drop",
aq_ta, aq_rcpt, aq_rcpt->aqr_flags,
aq_ta->aqt_rcpts_left);
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_WARN, 4,
"sev=WARN, func=q_upd_rcpt_ok, ss_ta=%p, idx=%u, bounce=doublebounce, status=drop"
, ss_ta_id, aq_rcpt->aqr_idx);
}
else if (SM_SUCCESS == rcpt_status)
{
time_T time_now;
time_now = evthr_time(qmgr_ctx->qmgr_ev_ctx);
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INFO, 10,
"sev=INFO, func=q_upd_rcpt_ok, rcpt_id=%s, rcpt=%@S, xdelay=%lu, delay=%lu"
, rcpt_id, aq_rcpt->aqr_pa
, time_now - aq_rcpt->aqr_last_try
, time_now - aq_rcpt->aqr_st_time);
}
QM_LEV_DPRINTFC(QDC_UPDRCPT, 1, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_ok, found rcpt=%p, ss_ta=%s, stat=%d, flags=%#x\n", aq_rcpt, ss_ta_id, aq_rcpt->aqr_status, aq_rcpt->aqr_flags));
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IQDB))
{
/* "Remove" rcpt from IBDB */
#if SM_IBDB_RCPT_REMOVE
ret = ibdb_rcpt_status(qmgr_ctx->qmgr_ibdb, ibdb_rcpt,
IBDB_RCPT_DONE, IBDB_FL_NOROLL, THR_LOCK_UNLOCK);
#else
ret = ibdb_rcpt_app(qmgr_ctx->qmgr_ibdb, ibdb_rcpt,
ibdb_req_hd, IBDB_RCPT_DONE);
#endif
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 3,
"sev=ERROR, func=q_upd_rcpt_ok, rcpt_id=%s, rcpt=%@S, ibdb_rcpt_status=%m",
rcpt_id, aq_rcpt->aqr_pa, ret);
goto error;
}
}
else if (AQR_IS_FLAG(aq_rcpt, AQR_FL_DEFEDB))
{
ret = edb_rcpt_rm_req(qmgr_ctx->qmgr_edb, rcpt_id, edb_req_hd);
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 1,
"sev=ERROR, func=q_upd_rcpt_ok, rcpt_id=%s, rcpt=%@S, edb_rcpt_rm_req=%m",
rcpt_id, aq_rcpt->aqr_pa, ret);
goto error;
}
else
{
QM_LEV_DPRINTFC(QDC_UPDRCPT, 5, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_ok, edb_rcpt_rm_req(%S, %s)=%r\n", aq_rcpt->aqr_pa, rcpt_id, ret));
AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_R);
}
}
#if QMGR_TEST
else if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_INT_SRC))
; /* internally generated transaction */
#endif
else if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DSN))
{
/* HACK ... special treatment for bounces */
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=ERROR, func=q_upd_rcpt_ok, rcpt_id=%s, rcpt_flag=%#x, status=from_unknown_queue",
rcpt_id, aq_rcpt->aqr_flags);
}
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC|AQR_FL_IS_DBNC))
ret = qda_upd_dsn(qmgr_ctx, aq_ta, aq_rcpt, ss_ta_id, edb_req_hd);
error:
/* currently no cleanup */
return ret;
}
/*
** Q_UPD_RCPT_STAT -- Update status for one recipient
**
** This function may append entries to the update queues for
** DEFEDB and IBDB which need to be taken care of by the caller.
**
** Parameters:
** qmgr_ctx -- QMGR context
** ss_ta_id -- SMTPS transaction id
** ta_status -- status of (entire) transaction
** err_st -- state which cause error
** aq_ta -- AQ transaction
** aq_rcpt -- AQ recipient
** edb_req_hd -- head of request list for (DEF)EDB
** ibdb_req_hd -- head of request list for IBDB
** errmsg -- error message (might be NULL)
** (must be "sanitized" by caller)
** piqdb_rcpts_done -- (pointer to) # of IQDB rcpts done (in/out)
** pdelay_next_try -- (pointer to) delay until next try (output)
**
** Returns:
** success: flags as shown in sm/qmgr-int.h
** to activate scheduler or SMAR.
** Alternatively this might be an output parameter.
** error: usual sm_error code
**
** Side Effects:
** on error: may modify aq_ta counters (aq_upd_ta_rcpt_cnts)
** and increase iqdb_rcpts_done,
** plus side effects of q_upd_rcpt_ok(), q_upd_rcpt_fail(),
** i.e., may write to ibdb, change ibdb/edb request lists.
**
** Called by: qda_upd_ta_rcpt_stat()
**
** Locking: aq_ctx and edbc must be locked.
**
** Last code review:
** Last code change:
*/
sm_ret_T
q_upd_rcpt_stat(qmgr_ctx_P qmgr_ctx, sessta_id_T ss_ta_id, sm_ret_T ta_status,
uint err_st, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt,
edb_req_hd_P edb_req_hd, ibdb_req_hd_P ibdb_req_hd, sm_str_P errmsg,
uint *piqdb_rcpts_done, int *pdelay_next_try)
{
sm_ret_T ret, rcpt_status, flags, rv;
aq_ctx_P aq_ctx;
ibdb_rcpt_T ibdb_rcpt;
rcpt_id_T rcpt_id;
SM_REQUIRE(piqdb_rcpts_done != NULL);
SM_IS_QMGR_CTX(qmgr_ctx);
SM_IS_AQ_TA(aq_ta);
SM_IS_AQ_RCPT(aq_rcpt);
aq_ctx = qmgr_ctx->qmgr_aq;
SM_IS_AQ(aq_ctx);
QM_LEV_DPRINTFC(QDC_UPDRCPT, 1, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_stat, stat=%d, err_st=%r, aqt_rcpts_inaq=%u, aqr_flags=%#x\n", ta_status, err_st, aq_ta->aqt_rcpts_inaq, aq_rcpt->aqr_flags));
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DA, QM_LMOD_DASTAT,
SM_LOG_DEBUG, 14,
"sev=DBG, func=q_upd_rcpt_stat, ss_ta=%s, rcpt=%@S, idx=%u, stat=%d, err_state=%#x",
aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_pa,
aq_rcpt->aqr_idx, ta_status, err_st);
rv = ret = SM_SUCCESS;
flags = 0;
QM_LEV_DPRINTFC(QDC_UPDRCPT, 5, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_stat, rcpt_da_id=%s, idx=%u, aq_rcpt=%p\n", aq_rcpt->aqr_da_ta_id, aq_rcpt->aqr_idx, aq_rcpt));
/*
** If there is a new recipient status use that,
** otherwise the transaction status.
*/
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_STAT_NEW))
{
rcpt_status = aq_rcpt->aqr_status_new;
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INFO, 8,
"sev=DBG, func=q_upd_rcpt_stat, rcpt=%@S, idx=%u, stat=%d",
aq_rcpt->aqr_pa, aq_rcpt->aqr_idx, rcpt_status);
}
else
rcpt_status = ta_status;
/* Set error state if necessary */
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_ERRST_UPD))
aq_rcpt->aqr_err_st = err_st;
if (aq_ctx->aq_t_da > 0)
--aq_ctx->aq_t_da;
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=q_upd_rcpt_stat, ss_ta=%s, idx=%s, aq_t_da=0, when=before_decreasing",
ss_ta_id, aq_rcpt->aqr_idx);
}
/* Compose rcpt_id for later use */
sm_snprintf(rcpt_id, sizeof(rcpt_id), SMTP_RCPTID_FORMAT,
ss_ta_id, aq_rcpt->aqr_idx);
if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IQDB))
{
/* Store data in ibdb_rcpt for update */
ibdb_rcpt.ibr_ta_id = ss_ta_id;
ibdb_rcpt.ibr_pa = aq_rcpt->aqr_pa;
ibdb_rcpt.ibr_idx = aq_rcpt->aqr_idx;
/*
** XXX Remove recipient from IQDB in all cases???
** Note: the recipient is safely in IBDB, so we can
** do this now (instead of after transferring the
** recipient to other queues if required).
** Are there any cases in which we would like to have
** fast access to the recipient despite the fact
** that we already tried to deliver it?
**
** XXX Why don't we remove the recipient from IQDB
** as soon as it is in AQ? (2003-02-07)
** Where is IQDB related data used after it has been
** copied into AQ? If it is used, can we refer to
** the copy in AQ instead?
**
** XXX Should we do this only after an "entire"
** delivery attempt has been made, i.e., if there
** there are still more destinations to try then
** don't remove the entry now?
**
** Remove it from iqdb in all cases no matter which
** result has been returned?
** It must be removed at least if
** - it has been successfully delivered
** - delivery failed permanently (-> trigger DSN)
** If delivery failed temporarily we could keep it in
** AQ depending on the scheduling strategy...
** Depending on the DSNs requested we may have to
** keep the transaction around. Hence we need different
** counters (do we?) or at least some flag that shows this.
*/
/*
** Other cases? For example: permanent failure?
*/
#define AQR_NO_RETRIES(aq_rcpt, rcpt_status) \
(SMTP_DONE(rcpt_status) || \
smtp_reply_type(rcpt_status) == SMTP_RTYPE_PERM || \
!AQR_MORE_DESTS(aq_rcpt) || AQR_DEFER(aq_rcpt))
if (AQR_NO_RETRIES(aq_rcpt, rcpt_status))
{
ret = iqdb_rcpt_rm(qmgr_ctx->qmgr_iqdb, rcpt_id,
SMTP_RCPTID_SIZE, THR_LOCK_UNLOCK);
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR,
(sm_error_value(ret) == SM_E_NOTFOUND) ? 8 : 1,
"sev=ERROR, func=q_upd_rcpt_stat, rcpt_id=%s, iqdb_rcpt_rm=%m",
rcpt_id, ret);
if (sm_error_value(ret) != SM_E_NOTFOUND)
{
rv = ret;
goto error;
}
}
else
{
/* One rcpt successfully removed from IQDB */
SM_ASSERT(*piqdb_rcpts_done < UINT_MAX);
++(*piqdb_rcpts_done);
}
}
}
/* Update counters in aq_ta */
#if QMGR_STATS
if (SMTP_OK == rcpt_status)
++qmgr_ctx->qmgr_rcpts_sent;
#endif
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_stat, flags=%#x, old=%d, new=%d\n", aq_rcpt->aqr_flags, aq_rcpt->aqr_status, rcpt_status));
ret = aq_upd_ta_rcpt_cnts(aq_ta, aq_rcpt->aqr_status, rcpt_status,
qmgr_ctx->qmgr_lctx);
SM_ASSERT(SM_SUCCESS == ret); /* OK, see aq_upd_ta_rcpt_cnts() */
/*
** Check whether recipient has been delivered ("taken care of"/"done")
** or is a double bounce that will be dropped on the floor, i.e.,
** delivery failed permanently or the item is too long in the queue.
*/
if (SMTP_DONE(rcpt_status) ||
(AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC) &&
(smtp_reply_type(rcpt_status) == SMTP_RTYPE_PERM ||
aq_rcpt->aqr_st_time + qmgr_ctx->qmgr_cnf.q_cnf_tmo_return
< evthr_time(qmgr_ctx->qmgr_ev_ctx))))
{
ret = q_upd_rcpt_ok(qmgr_ctx, ss_ta_id, rcpt_status, aq_ta,
aq_rcpt, &ibdb_rcpt, rcpt_id, edb_req_hd, ibdb_req_hd);
if (sm_is_err(ret))
{
rv = ret;
goto error;
}
}
else
{
ret = q_upd_rcpt_fail(qmgr_ctx, ss_ta_id, rcpt_status,
aq_ta, aq_rcpt, &ibdb_rcpt, rcpt_id,
edb_req_hd, ibdb_req_hd, errmsg, pdelay_next_try);
if (!sm_is_err(ret))
flags |= ret;
else
{
rv = ret;
goto error;
}
}
/*
** Do this only if really necessary:
** 1. rcpt has been successfully delivered
** 2. rcpt failed and no more chances for delivery now (see above).
*/
QM_LEV_DPRINTFC(QDC_UPDRCPT, 1, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_stat, aq_rcpt=%p, ss_ta=%s, idx=%u, cur=%d/max=%d, no_retries=%d\n", aq_rcpt, ss_ta_id, aq_rcpt->aqr_idx, aq_rcpt->aqr_addr_cur, aq_rcpt->aqr_addr_max, AQR_NO_RETRIES(aq_rcpt, rcpt_status)));
if (AQR_NO_RETRIES(aq_rcpt, rcpt_status))
{
/* Remove recipient from AQ */
#if QMGR_DEBUG > 1
QM_LEV_DPRINTFC(QDC_UPDRCPT, 3, (QM_DEBFP, "sev=DBG, func=q_upd_rcpt_stat, remove aq_rcpt=%p\n", aq_rcpt));
aq_rcpt_print(aq_rcpt);
#endif
ret = aq_rcpt_rm(aq_ctx, aq_rcpt, AQR_RM_N_RDQ);
if (sm_is_err(ret))
{
/* what to do now? */
QM_LEV_DPRINTFC(QDC_UPDRCPT, 0, (QM_DEBFP, "sev=ERROR, func=q_upd_rcpt_stat, aq_rcpt_rm=%r\n", ret));
}
else
{
if (aq_ta->aqt_rcpts_inaq > 0)
--aq_ta->aqt_rcpts_inaq;
else
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_INCONS, 2,
"sev=FATAL, func=q_upd_rcpt_stat, aq_ta=%p, ac_rcpt=%p, aqt_rcpts_inaq=%d, when=before_decreasing",
aq_ta, aq_rcpt, aq_ta->aqt_rcpts_inaq);
}
}
}
else
{
ret = aq_waitq_rm(aq_ctx, aq_rcpt, AQWQ_ANY, false);
if (sm_is_err(ret))
QM_LEV_DPRINTFC(QDC_UPDRCPT, 0, (QM_DEBFP, "sev=ERROR, func=q_upd_rcpt_stat, aq_waitq_rm=%r\n", ret));
/* ignore any error; see fct about possible problems */
/* Let's try the next address... */
aq_rcpt->aqr_addr_cur++;
ret = aq_rdq_add(aq_ctx, aq_rcpt, NULL, THR_NO_LOCK);
if (sm_is_err(ret))
{
rv = ret;
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_DASTAT, QM_LMOD_DASTAT,
SM_LOG_ERR, 4,
"sev=ERROR, func=q_upd_rcpt_stat, ac_rcpt=%p, aq_rdq_add=%m",
aq_rcpt, ret);
goto error;
}
/* Reset some flags; XXX more? */
AQR_CLR_FLAG(aq_rcpt, AQR_FL_SCHED|AQR_FL_WAIT4UPD|AQR_FL_STAT_NEW|AQR_FL_ERRST_UPD);
flags |= QDA_FL_ACT_SCHED;
/* remove entry from delivery list */
AQR_DA_DELENTRY(aq_rcpt);
}
error:
return sm_is_err(rv) ? rv : flags;
}
syntax highlighted by Code2HTML, v. 0.9.1