/*
 * 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