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