/*
* 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: edbs.c,v 1.27 2006/04/02 06:34:18 ca Exp $")
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"
#include "sm/edbc.h"
#include "sm/qmgr-int.h"
#include "sm/actdb-int.h"
#include "qmgr.h"
#include "log.h"
/*
** QM_EDB_SCAN -- scan EDB to fill EDBC
**
** Parameters:
** qmgr_ctx -- QMGR context
**
** Returns:
** usual sm_error code
**
** Locking:
** Locks edbc during entire operation.
** This will perform disk I/O, hence it may block other
** functions that try to access edbc for a longer time!
*/
/*
split this into
_init(qmgr_ctx, &scan_ctx)
_scan_n(qmgr_ctx, scan_ctx, n)
which scans only a certain number of entries?
problem: what happens if some other function modifies EDB?
will the "cursor" miss the new entries? Yes:
"Modifications to the database during a sequential scan will be
reflected in the scan; that is, records inserted behind a cursor will
not be returned while records inserted in front of a cursor will be
returned."
*/
sm_ret_T
qm_edb_scan(qmgr_ctx_P qmgr_ctx)
{
sm_ret_T ret, rt, rv;
int r;
bool ok;
id_count_T idc;
edb_ctx_P edb_ctx;
edbc_ctx_P edbc_ctx;
edb_req_P edb_req;
aq_rcpt_P aq_rcpt;
edb_cursor_P edb_cursor;
rcpt_id_T rcpt_id;
edb_ctx = qmgr_ctx->qmgr_edb;
edbc_ctx = qmgr_ctx->qmgr_edbc;
edb_req = NULL;
aq_rcpt = NULL;
edb_cursor = NULL;
ok = true;
rv = SM_SUCCESS;
/*
** Is it ok to keep EDBC locked? It's used in da_stat.
** However, if we don't keep it locked the order might be
** screwed up.
*/
r = pthread_mutex_lock(&edbc_ctx->edbc_mutex);
SM_LOCK_OK(r);
if (r != 0)
{
QM_LEV_DPRINTFC(QDC_EDBS, 0, (QM_DEBFP, "sev=ERROR, func=qm_edb_scan, lock edbc=%d\n", r));
return sm_error_perm(SM_EM_EDB, r);
}
ret = aq_rcpt_new(&aq_rcpt);
if (sm_is_err(ret))
goto error;
ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, false);
if (sm_is_err(ret))
goto error;
ret = edb_rd_open(edb_ctx, &edb_cursor);
if (sm_is_err(ret))
goto error;
while (ok)
{
ret = edb_rd_next(edb_ctx, edb_cursor, edb_req);
if (sm_is_err(ret))
{
if (ret == sm_error_perm(SM_EM_EDB, DB_NOTFOUND))
break;
goto error;
}
rt = edb_get_type(edb_req);
if (sm_is_err(ret))
goto error;
else if (rt == EDB_REQ_RCPT)
{
ret = edb_rcpt_dec(edb_req, aq_rcpt);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_ss_ta_id[SMTP_STID_SIZE - 1] = '\0';
ret = sm_id2idc(aq_rcpt->aqr_ss_ta_id, &idc);
if (ret == SM_SUCCESS && qmgr_ctx->qmgr_idc < idc)
qmgr_ctx->qmgr_idc = idc;
sm_snprintf(rcpt_id, sizeof(rcpt_id),
SMTP_RCPTID_FORMAT,
aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_idx);
rv = edbc_add(edbc_ctx, rcpt_id,
aq_rcpt->aqr_next_try, true);
if (sm_is_err(rv))
{
ok = false;
QMGR_SET_SFLAG(qmgr_ctx, QMGR_SFL_EDBC);
if (sm_error_value(rv) == ENOMEM)
QMGR_SET_RFLAG(qmgr_ctx,
QMGR_RFL_MEM|QMGR_RFL_EBDC);
else if (rv == 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_EDBC, QM_LMOD_EDBS,
SM_LOG_ERR, 2,
"sev=ERROR, func=qm_edb_scan, entries=%u, edbc_add=%m"
, qmgr_ctx->qmgr_edbc->edbc_entries
, rv);
}
else
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_EDBC, QM_LMOD_EDBS,
SM_LOG_INFO, 12,
"sev=INFO, func=qm_edb_scan, rcpt_id=%s, entries=%u"
, rcpt_id
, qmgr_ctx->qmgr_edbc->edbc_entries);
/*
** Free some data that is allocated by
** edb_rcpt_dec().
** Note: either pass a parameter to edb_rcpt_dec()
** such that it doesn't allocate this data
** (because it isn't needed here), or provide
** a "free" routine for this.
*/
SM_STR_FREE(aq_rcpt->aqr_pa);
if (aq_rcpt->aqr_addrs != NULL
&& !AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR)
&& aq_rcpt->aqr_addrs != &aq_rcpt->aqr_addr_mf)
SM_FREE(aq_rcpt->aqr_addrs);
}
else if (rt != EDB_REQ_TA && rt != EDB_REQ_VRS)
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_EDBC, QM_LMOD_EDBS,
SM_LOG_ERR, 3,
"sev=ERROR, func=qm_edb_scan, unknown_type=%#x"
, rt);
}
}
if (edb_ctx != NULL && edb_cursor != NULL)
{
ret = edb_rd_close(edb_ctx, edb_cursor);
if (sm_is_err(ret))
{
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_EDBC, QM_LMOD_EDBS,
SM_LOG_ERR, 3,
"sev=ERROR, func=qm_edb_scan, edb_rd_close=%m"
, ret);
}
}
aq_rcpt_free(aq_rcpt);
if (edb_req != NULL)
(void) edb_req_rel(edb_ctx, edb_req, 0, THR_NO_LOCK);
r = pthread_mutex_unlock(&edbc_ctx->edbc_mutex);
if (r != 0)
{
QM_LEV_DPRINTFC(QDC_EDBS, 0, (QM_DEBFP, "sev=ERROR, func=qm_edb_scan, unlock edbc=%d\n", r));
SM_ASSERT(r == 0);
}
/* return an error if !ok? */
return ok ? SM_SUCCESS : rv;
error:
(void) edb_req_rel(edb_ctx, edb_req,
(sm_is_err(ret) && sm_error_value(ret) == ENOMEM)
? EDB_RQF_FREE : 0,
THR_LOCK_UNLOCK);
r = pthread_mutex_unlock(&edbc_ctx->edbc_mutex);
SM_ASSERT(r == 0);
aq_rcpt_free(aq_rcpt);
sm_log_write(qmgr_ctx->qmgr_lctx,
QM_LCAT_EDBC, QM_LMOD_EDBS,
SM_LOG_ERR, 3,
"sev=ERROR, func=qm_edb_scan, result=%m"
, ret);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1