/*
* 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: rdibdb.c,v 1.74 2007/06/18 04:42:31 ca Exp $")
#include "sm/error.h"
#include "sm/magic.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/ibdb.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "sm/bhtable.h"
#include "sm/rdibdb.h"
#include "sm/qmibdb.h"
#include "liblog.h"
#if SM_TEST_PRT
#include "sm/io.h"
#endif
/* HACK (used by test programs that include this file) */
#define BHTSIZE (32 * 1024)
/*
** Problems:
** time isn't directly available: file mtime could be used,
** for now: current time.
*/
/*
** QM_IBDB_TA_CLEAN -- Clean out IBDB TA structure for reuse
** Reset values in IBDB TA to be used again for ibdbr_get()
** (which will create cdb_id itself).
**
** Parameters:
** ibdb_ta -- IBDB TA
**
** Returns:
** none.
*/
void
qm_ibdb_ta_clean(ibdb_ta_P ibdb_ta)
{
SM_REQUIRE(ibdb_ta);
SM_CSTR_FREE(ibdb_ta->ibt_cdb_id);
}
/* context for ta_cancel */
typedef struct tac_ctx_S tac_ctx_T, *tac_ctx_P;
struct tac_ctx_S
{
#if SM_TAC_CTX_CHECK
sm_magic_T sm_magic;
#endif
bht_P tacx_bht_rcpt; /* bhtable RCPT */
char *tacx_ta_id;
};
#if SM_TAC_CTX_CHECK
# define SM_IS_TAC_CTX(tac_ctx) SM_REQUIRE_ISA((tac_ctx), SM_TAC_CTX_MAGIC)
#else
# define SM_IS_TAC_CTX(tac_ctx) SM_REQUIRE((tac_ctx) != NULL)
#endif
/*
** TA_CANCEL -- Cancel all recipients of a transaction (callback function)
**
** Parameters:
** info -- bht entry
** ctx -- TA ID
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
ta_cancel_rcpts(bht_entry_P info, void *ctx)
{
ibdb_rcpt_P ibdb_rcpt;
sessta_id_P ta_id;
tac_ctx_P tac_ctx;
SM_REQUIRE(info != NULL);
SM_REQUIRE(ctx != NULL);
ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value;
SM_REQUIRE(ibdb_rcpt->ibr_ta_id != NULL);
tac_ctx = (tac_ctx_P) ctx;
SM_IS_TAC_CTX(tac_ctx);
ta_id = tac_ctx->tacx_ta_id;
if (SESSTA_EQ(ibdb_rcpt->ibr_ta_id, ta_id))
{
char rcpt_id[SMTP_RCPTID_SIZE + 1];
#if SM_TEST_PRT
if (debug > 1)
{
sm_io_fprintf(smioout, "cancel TA: ta=%s, ", ta_id);
sm_io_fprintf(smioout, "rcpt_idx=%d, ",
ibdb_rcpt->ibr_idx);
sm_io_fprintf(smioout, "\n");
}
#endif /* SM_TEST_PRT */
sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT,
ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx);
bht_rm(tac_ctx->tacx_bht_rcpt, rcpt_id, SMTP_RCPTID_SIZE,
ibdb_rcpt_free, NULL);
}
return SM_SUCCESS;
}
/*
** QM_IBDB_CANCEL_TA -- Cancel a transaction
**
** Parameters:
** bht_ta -- bhtable (TA)
** bht_rcpt -- bhtable (RCPT)
** ta_id -- TA ID
**
** Returns:
** usual sm_error code
**
** Note: unfortunately it is required to "walk" through the entire
** recipient hash table and find matching entries because the
** key is rcpt_id, not ta_id, hence lookups of ta_id can't
** find anything. Even if bhtable would have a "lookup sub key"
** function, it wouldn't work to find all matching entries:
** due to the hashing they are (hopefully) spread over the table.
** a different map type (btree) would allow for a faster access
** method (maybe use BDB btree, in memory only?).
*/
sm_ret_T
qm_ibdb_cancel_ta(bht_P bht_ta, bht_P bht_rcpt, sessta_id_P ta_id)
{
tac_ctx_T tac_ctx;
tac_ctx.tacx_bht_rcpt = bht_rcpt;
tac_ctx.tacx_ta_id = ta_id;
(void) bht_walk(bht_rcpt, ta_cancel_rcpts, (void *) &tac_ctx);
(void) bht_rm(bht_ta, ta_id, SMTP_STID_SIZE, ibdb_ta_free, NULL);
return SM_SUCCESS;
}
/*
** QM_IBDB_TA_ACTION -- Add transaction to DEFEDB (callback function)
**
** Parameters:
** info -- bht entry
** ctx -- context
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qm_ibdb_ta_action(bht_entry_P info, void *ctx)
{
ibdb_ta_P ibdb_ta;
ri_ctx_P ri_ctx;
aq_ta_P aq_ta;
#if !SM_TEST_PRT
sm_ret_T ret;
qmgr_ctx_P qmgr_ctx;
edb_req_P edb_req;
#endif /* !SM_TEST_PRT */
/*
** Do NOT return an error code because that will stop
** the recovery (bht_walk()).
*/
ri_ctx = (ri_ctx_P) ctx;
ibdb_ta = (ibdb_ta_P) info->bhe_value;
if (ibdb_ta->ibt_ta_id == NULL)
return SM_SUCCESS;
/* sm_error_perm(SM_EM_Q_RDIBDB, SM_E_UNEXPECTED); */
#if !SM_TEST_PRT
/* check whether TA already in DEFEDB */
qmgr_ctx = ri_ctx->rix_qmgr_ctx;
if (qmgr_ctx != NULL)
{
SM_IS_QMGR_CTX(qmgr_ctx);
edb_req = ri_ctx->rix_edb_req;
SESSTA_COPY(edb_req->edb_req_id, ibdb_ta->ibt_ta_id);
edb_req->edb_req_type = EDB_REQ_TA;
ret = edb_rd_req(qmgr_ctx->qmgr_edb, edb_req);
if (ret == SM_SUCCESS)
{
/* the data in EDB is ok, no need to change it */
return ret;
}
}
#endif /* !SM_TEST_PRT */
aq_ta = ri_ctx->rix_aq_ta;
aq_ta->aqt_st_time = ri_ctx->rix_time;
aq_ta->aqt_rcpts_inaq = ibdb_ta->ibt_nrcpts;
aq_ta->aqt_rcpts_tot = ibdb_ta->ibt_nrcpts;
aq_ta->aqt_nxt_idx = ibdb_ta->ibt_nrcpts;
aq_ta->aqt_rcpts_tot = ibdb_ta->ibt_nrcpts;
aq_ta->aqt_rcpts_left = ibdb_ta->ibt_rcpts_left;
aq_ta->aqt_rcpts_temp = ibdb_ta->ibt_rcpts_temp;
aq_ta->aqt_rcpts_perm = ibdb_ta->ibt_rcpts_perm;
aq_ta->aqt_rcpts_tried = ibdb_ta->ibt_nrcpts - ibdb_ta->ibt_rcpts_left;
SESSTA_COPY(aq_ta->aqt_ss_ta_id, ibdb_ta->ibt_ta_id);
aq_ta->aqt_cdb_id = ibdb_ta->ibt_cdb_id;
/* who is responsible for CDB ID? should we use CSTR_DUP? */
aq_ta->aqt_mail->aqm_pa = ibdb_ta->ibt_mail_pa;
/* "transfer" header of list to aq_ta */
aq_ta->aqt_hdrmodhd = ibdb_ta->ibt_hdrmodhd;
ibdb_ta->ibt_hdrmodhd = NULL;
#if !SM_TEST_PRT
if (qmgr_ctx != NULL)
{
/* Which status?? */
ret = edb_ta_app(qmgr_ctx->qmgr_edb, aq_ta,
&ri_ctx->rix_edb_req_hd, 0 /* XXX */);
if (sm_is_err(ret))
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP,
"sev=ERROR, func=qm_ibdb_ta_action, ss_ta=%s, edb_ta_app=%m\n",
ibdb_ta->ibt_ta_id, ret));
}
#endif /* !SM_TEST_PRT */
#if SM_TEST_PRT
if (debug > 1)
{
sm_io_fprintf(smioout, "TA: ta=%s, ", ibdb_ta->ibt_ta_id);
sm_io_fprintf(smioout, "cdb=%C, ", ibdb_ta->ibt_cdb_id);
sm_io_fprintf(smioout, "mail_pa=%S, ", aq_ta->aqt_mail->aqm_pa);
sm_io_fprintf(smioout, "rcpts_tot=%d, ", aq_ta->aqt_rcpts_tot);
sm_io_fprintf(smioout, "rcpts_left=%d, ", aq_ta->aqt_rcpts_left);
sm_io_fprintf(smioout, "\n");
}
#endif /* SM_TEST_PRT */
aq_ta->aqt_mail->aqm_pa = NULL;
SM_STR_FREE(ibdb_ta->ibt_mail_pa);
return SM_SUCCESS;
}
/*
** QM_IBDB_RCPT_ACTION -- Add recipient to DEFEDB (callback function)
**
** Parameters:
** info -- bht entry
** ctx -- context
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qm_ibdb_rcpt_action(bht_entry_P info, void *ctx)
{
sm_ret_T ret;
ibdb_rcpt_P ibdb_rcpt;
ibdb_ta_P ibdb_ta;
ri_ctx_P ri_ctx;
aq_ta_P aq_ta;
aq_rcpt_P aq_rcpt;
qmgr_ctx_P qmgr_ctx;
edb_req_P edb_req;
/*
** Do NOT return an error code because that will stop
** the recovery (bht_walk()).
*/
ri_ctx = (ri_ctx_P) ctx;
ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value;
if (ibdb_rcpt->ibr_ta_id == NULL)
return SM_SUCCESS; /* sm_error_perm(SM_EM_Q_RDIBDB, SM_E_UNEXPECTED); */
aq_ta = ri_ctx->rix_aq_ta;
aq_rcpt = ri_ctx->rix_aq_rcpt;
ibdb_ta = bht_find(ri_ctx->rix_bht_ta, ibdb_rcpt->ibr_ta_id,
SMTP_STID_SIZE);
if (ibdb_ta == NULL)
{
/*
** XXX OOPS what's going on? Where's our TA?
** Under which circumstances can this happen?
*/
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "ERROR: bht_find(%s) failed\n", ibdb_rcpt->ibr_ta_id));
#if QM_DEBUG_RDIBDB
/* let's crash ... */
SM_ASSERT(ibdb_ta != NULL);
#endif /* QM_DEBUG_RDIBDB */
return SM_SUCCESS; /* sm_error_perm(SM_EM_Q_RDIBDB, SM_E_NOTFOUND); */
}
#if !SM_TEST_PRT
/* check whether rcpt already in DEFEDB */
qmgr_ctx = ri_ctx->rix_qmgr_ctx;
if (qmgr_ctx != NULL)
{
SM_IS_QMGR_CTX(qmgr_ctx);
edb_req = ri_ctx->rix_edb_req;
RCPT_ID_COPY(edb_req->edb_req_id, info->bhe_key);
edb_req->edb_req_type = EDB_REQ_RCPT;
ret = edb_rd_req(qmgr_ctx->qmgr_edb, edb_req);
/* found: ignore it (XXX not even counting?) */
if (ret == SM_SUCCESS)
return ret;
}
#endif /* !SM_TEST_PRT */
ibdb_ta->ibt_rcpts_left++;
/* Let's copy it over... */
SESSTA_COPY(aq_rcpt->aqr_ss_ta_id, ibdb_rcpt->ibr_ta_id);
aq_rcpt->aqr_pa = ibdb_rcpt->ibr_pa;
aq_rcpt->aqr_da_idx = 0;
aq_rcpt->aqr_st_time = ri_ctx->rix_time;
aq_rcpt->aqr_entered = ri_ctx->rix_time;
aq_rcpt->aqr_idx = ibdb_rcpt->ibr_idx;
aq_rcpt->aqr_status = AQR_ST_NEW;
AQR_DA_INIT(aq_rcpt);
AQR_SS_INIT(aq_rcpt);
#if !SM_TEST_PRT
if (qmgr_ctx != NULL)
{
ret = edb_rcpt_app(qmgr_ctx->qmgr_edb, aq_rcpt,
&ri_ctx->rix_edb_req_hd,
aq_rcpt->aqr_status);
if (sm_is_err(ret))
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_ibdb_rcpt_action, rcpt_id=%s, edb_rcpt_app=%r\n",
ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx,
ret));
}
#endif /* !SM_TEST_PRT */
#if SM_TEST_PRT
if (debug > 1)
{
sm_io_fprintf(smioout, "RCPT: ta=%s, ", ibdb_rcpt->ibr_ta_id);
sm_io_fprintf(smioout, "rcpt_pa=%S, ", aq_rcpt->aqr_pa);
sm_io_fprintf(smioout, "rcpt_idx=%d, ", ibdb_rcpt->ibr_idx);
sm_io_fprintf(smioout, "\n");
}
#endif /* SM_TEST_PRT */
aq_rcpt->aqr_pa = NULL;
SM_STR_FREE(ibdb_rcpt->ibr_pa);
return SM_SUCCESS;
}
/*
** QM_RIBDB -- read INCEDB records and discard "closed" transactions.
**
** Parameters:
** ri_ctx -- Recover-IBDB context
**
** Returns:
** usual sm_error code
**
** Input: ri_ctx contains initialized values for:
** rix_ibdbrc
** rix_edb_req_hd
** rix_bht_ta
** rix_bht_rcpt
** rix_ntas
** rix_nrcpts
**
** Output: ri_ctx contains (possibly) updated values for:
** rix_edb_req_hd
** rix_bht_ta
** rix_bht_rcpt
** rix_ntas
** rix_nrcpts
** rix_last_id
**
** Notes:
** - this function reads _all_ entries into memory; it removes
** transactions that are discarded, but keeps all open
** transactions in memory. there should be only a few of those,
** hence memory should be small. if this ever turns out to
** be a problem, those entries could be written directly to
** defedb (and removed if the transaction is closed/cancelled).
** - currently this function calls bht_walk for TA and RCPT,
** hence the other values in ri_ctx must be initialized too.
*/
sm_ret_T
qm_ribdb(ri_ctx_P ri_ctx)
{
int status;
sm_ret_T ret;
bool more;
id_count_T idc;
ibdbr_ctx_P ibdbrc;
ibdb_ta_P ibdb_ta, ibdb_lta;
ibdb_rcpt_P ibdb_rcpt, ibdb_lrcpt;
ibdb_hdrmod_P ibdb_hdrmod;
sm_hdrmod_P sm_hdrmod;
bht_P bht_ta, bht_rcpt;
bht_entry_P bhte;
rcpt_id_P rcpt_id;
ibdbrc = ri_ctx->rix_ibdbrc;
bht_ta = ri_ctx->rix_bht_ta;
bht_rcpt = ri_ctx->rix_bht_rcpt;
ret = qm_ibdb_ta_new(&ibdb_ta);
if (sm_is_err(ret) || ibdb_ta == NULL)
goto end;
ret = qm_ibdb_rcpt_new(&ibdb_rcpt);
if (sm_is_err(ret) || ibdb_rcpt == NULL)
goto end;
ret = ibdbr_hdrmod_new(&ibdb_hdrmod);
if (sm_is_err(ret) || ibdb_hdrmod == NULL)
goto end;
more = true;
do
{
ret = ibdbr_get(ibdbrc, ibdb_rcpt, ibdb_ta, ibdb_hdrmod
, &status);
switch (ret)
{
case RT_IBDB_TA:
++ri_ctx->rix_ntas;
if (status == IBDB_TA_NEW)
{
ret = sm_id2idc(ibdb_ta->ibt_ta_id, &idc);
if (ret == SM_SUCCESS &&
ri_ctx->rix_last_id < idc)
ri_ctx->rix_last_id = idc;
ret = bht_add(bht_ta,
ibdb_ta->ibt_ta_id, SMTP_STID_SIZE,
ibdb_ta, &bhte);
if (sm_is_err(ret))
{
/* XXX what to do now? */
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_ribdb, bht_add=%r, ta=%s\n", ret, ibdb_ta->ibt_ta_id));
goto end;
}
ret = qm_ibdb_ta_new(&ibdb_ta);
if (sm_is_err(ret) || ibdb_ta == NULL)
goto end;
}
else if (status == IBDB_TA_CANCEL)
{
qm_ibdb_cancel_ta(bht_ta, bht_rcpt,
ibdb_ta->ibt_ta_id);
qm_ibdb_ta_clean(ibdb_ta);
}
else
{
ibdb_lta = bht_find(bht_ta,
ibdb_ta->ibt_ta_id, SMTP_STID_SIZE);
qm_ibdb_ta_clean(ibdb_ta);
/* might happen if some logfiles are removed */
if (ibdb_lta == NULL)
break;
bht_rm(bht_ta, ibdb_ta->ibt_ta_id,
SMTP_STID_SIZE, ibdb_ta_free, NULL);
}
break;
case RT_IBDB_RCPT:
++ri_ctx->rix_nrcpts;
if (status == IBDB_RCPT_NEW)
{
ret = sm_id2idc(ibdb_rcpt->ibr_ta_id, &idc);
if (ret == SM_SUCCESS &&
ri_ctx->rix_last_id < idc)
ri_ctx->rix_last_id = idc;
rcpt_id = (char *) sm_malloc(SMTP_RCPTID_SIZE
+ 1);
if (rcpt_id == NULL)
{
ret = sm_error_temp(SM_EM_Q_RDIBDB,
ENOMEM);
goto end;
}
sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE,
SMTP_RCPTID_FORMAT,
ibdb_rcpt->ibr_ta_id,
ibdb_rcpt->ibr_idx);
ret = bht_add(bht_rcpt,
rcpt_id, SMTP_RCPTID_SIZE,
ibdb_rcpt, &bhte);
if (sm_is_err(ret))
{
/* XXX what to do now? */
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_ribdb, bht_add=%r, rcpt_id=%s\n", ret, rcpt_id));
goto end;
}
ret = qm_ibdb_rcpt_new(&ibdb_rcpt);
if (sm_is_err(ret) || ibdb_rcpt == NULL)
goto end;
}
else
{
rcpt_id_T lrcpt_id;
sm_snprintf(lrcpt_id, SMTP_RCPTID_SIZE,
SMTP_RCPTID_FORMAT,
ibdb_rcpt->ibr_ta_id,
ibdb_rcpt->ibr_idx);
ibdb_lrcpt = bht_find(bht_rcpt,
lrcpt_id, SMTP_RCPTID_SIZE);
/* might happen if some logfiles are removed */
if (ibdb_lrcpt == NULL)
break;
ibdb_lta = bht_find(bht_ta,
ibdb_rcpt->ibr_ta_id,
SMTP_STID_SIZE);
if (ibdb_lta != NULL)
{
if (status == IBDB_RCPT_TEMP)
ibdb_lta->ibt_rcpts_temp++;
else if (status == IBDB_RCPT_PERM)
ibdb_lta->ibt_rcpts_perm++;
}
bht_rm(bht_rcpt, lrcpt_id, SMTP_RCPTID_SIZE,
ibdb_rcpt_free, NULL);
}
break;
case RT_IBDB_HDRMOD:
ibdb_lta = bht_find(bht_ta, ibdb_hdrmod->ibh_ta_id,
SMTP_STID_SIZE);
if (ibdb_lta == NULL)
{
/* clean up ibdb_hdrmod? */
SM_CSTR_FREE(ibdb_hdrmod->ibh_hdr);
break;
}
ret = sm_hdrmod_new(&ibdb_lta->ibt_hdrmodhd, true,
&sm_hdrmod);
if (sm_is_err(ret))
{
ret = sm_error_temp(SM_EM_Q_RDIBDB, ENOMEM);
goto end;
}
sm_hdrmod->sm_hm_type = ibdb_hdrmod->ibh_type;
sm_hdrmod->sm_hm_pos = ibdb_hdrmod->ibh_pos;
sm_hdrmod->sm_hm_hdr = ibdb_hdrmod->ibh_hdr;
ibdb_hdrmod->ibh_hdr = NULL;
break;
default:
if (sm_is_err(ret) && sm_error_value(ret) == ENOENT)
ret = SM_SUCCESS;
else if (SM_IO_EOF == ret || sm_error_perm(SM_EM_IO, EIO) == ret) {
/*
** This may happen if an entry wasn't written
** completely. Just ignore that entry.
*/
sm_log_write(ri_ctx->rix_qmgr_ctx->qmgr_lctx,
LIBQM_LCAT_LIB, LIBQM_LMOD_RDIBDB,
SM_LOG_WARN, 2,
"sev=WARN, func=qm_ribdb, ibdbr_get=%m"
, ret);
ret = SM_SUCCESS;
}
else
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_ribdb, ibdbr_get=%r\n", ret));
more = false;
break;
}
} while (more);
/* check recipients first to count them for TAs */
bht_walk(bht_rcpt, qm_ibdb_rcpt_action, ri_ctx);
/* Now run through open transactions */
bht_walk(bht_ta, qm_ibdb_ta_action, ri_ctx);
end:
qm_ibdb_ta_free(ibdb_ta);
ibdb_ta = NULL;
qm_ibdb_rcpt_free(ibdb_rcpt);
ibdb_rcpt = NULL;
ibdbr_hdrmod_free(ibdb_hdrmod);
return ret;
}
/*
** QM_RDIBDB -- read INCEDB records and discard "closed" transactions.
**
** Parameters:
** qmgr_ctx -- QMGR context
**
** Returns:
** usual sm_error code
**
** Notes:
** This should be done before qm_edb_scan() such that the
** new entries will be in EDBC. If it is done afterwards,
** EDBC must be populated too.
**
** If this function runs as a thread concurrently with
** the rest of QMGR then it also must provide proper locking
** for EDBC. Running this as separate thread at startup
** allows for a faster startup time because QMGR becomes
** faster operational, but it requires that the IBDB
** files are named properly to avoid overwriting etc.
**
** The current algorithm suggests splitting this into
** two functions or at least pass a parameter such that
** reading an existing recovery directory happens only
** on the first call (which need to be done before IBDB
** is opened unless some fancy, complicated renaming is used).
** Then IBDB is renamed to IBDB_R and that directory can
** be read while QMGR is running.
** Question: do we need to preserve the state (bhtables)
** between calls in that case?
**
** The main algorithm of this function is required in other
** places too, e.g., the cleanup process. Hence this should be
** broken apart and put into a library...
*/
sm_ret_T
qm_rdibdb(qmgr_ctx_P qmgr_ctx)
{
uint32_t first, last;
sm_ret_T ret, ibdb_which, res;
bool once;
ibdbr_ctx_P ibdbrc;
ri_ctx_P ri_ctx;
SM_IS_QMGR_CTX(qmgr_ctx);
/* Initialize most variables */
ibdbrc = NULL;
ibdb_which = 0;
once = false;
ret = qm_ri_ctx_new(qmgr_ctx, &ri_ctx);
if (sm_is_err(ret))
{
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_rdibdb, qm_ri_ctx_new=%r\n", ret));
goto end;
}
again:
/*
** Prepare for recovery: determine whether IBDB or recovery IBDB
** exists. If the latter exists: just return SM_IBDB_RCVR_EXISTS;
** otherwise rename IBDB to recovery IBDB.
*/
ibdb_which = ibdbrcvr_open();
QM_LEV_DPRINTFC(QDC_IBDBR, 4, (QM_DEBFP, "sev=INFO, func=qm_rdibdb, status=running, once=%d, ibdbrcvr_open=%r\n", once, ibdb_which));
if (sm_is_err(ibdb_which))
{
ret = ibdb_which;
goto end;
}
/* if neither directory exists: stop now */
if (ibdb_which == SM_IBDB_NO_RCVR)
goto end;
/* open IBDB recovery directory */
ret = ibdbf_get_seq((const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
IBDB_NAME, IBDB_OFL_RCVR, &first, &last);
QM_LEV_DPRINTFC(QDC_IBDBR, 4, (QM_DEBFP, "sev=DBG, func=qm_rdibdb, ibdbf_get_seq=%#x, first=%u, last=%u\n", ret, first, last));
/* distinguish error values?? e.g., no directory? */
if (sm_is_err(ret))
{
ret = SM_SUCCESS;
goto end; /* XXX no data? */
}
ret = ibdbr_open((const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
IBDB_NAME, SM_IO_RDONLY, first, 0 /* unused */,
IBDB_OFL_RCVR, &ibdbrc);
if (sm_is_err(ret))
goto end;
ri_ctx->rix_ibdbrc = ibdbrc;
/* evthr_time() isn't working yet; the system isn't running. */
ri_ctx->rix_time = time(NULLT);
#if !SM_TEST_PRT
ret = edb_req_new(qmgr_ctx->qmgr_edb, EDB_RQF_NONE,
&ri_ctx->rix_edb_req, true);
if (sm_is_err(ret))
goto end;
QM_LEV_DPRINTFC(QDC_IBDBR, 5, (QM_DEBFP, "rix_time=%ld\n", (long) ri_ctx->rix_time));
#endif
/* Actually read IBDB */
ret = qm_ribdb(ri_ctx);
if (sm_is_err(ret))
goto end;
if (qmgr_ctx->qmgr_idc < ri_ctx->rix_last_id)
qmgr_ctx->qmgr_idc = ri_ctx->rix_last_id;
#if !SM_TEST_PRT
/* write all TAs and RCPTs to DEFEDB */
ret = edb_wr_status(qmgr_ctx->qmgr_edb, &ri_ctx->rix_edb_req_hd);
if (sm_is_err(ret))
{
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_rdibdb, edb_wr_status=%r\n", ret));
goto end;
}
#endif
if (first <= last && ret == SM_SUCCESS)
(void) ibdbr_unlink(ibdbrc, first, last);
end:
if (ibdbrc != NULL)
{
res = ibdbr_close(ibdbrc);
/* Complain on error?? */
if (sm_is_err(res))
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "sev=ERROR, func=qm_rdibdb, ibdbr_close=%r\n", res));
ibdbrc = NULL;
}
/* check result?? */
res = ibdbrcvr_close();
QM_LEV_DPRINTFC(QDC_IBDBR, 2, (QM_DEBFP, "sev=DBG, func=qm_rdibdb, ibdbrcvr_close=%r\n", res));
/*
** If the recovery directory exists then it was read first, hence
** in the next step the "normal" directory must be read.
*/
if (ibdb_which == SM_IBDB_RCVR_EXISTS)
{
if (once)
{
/* oops ... been there, done that? */
QM_LEV_DPRINTFC(QDC_IBDBR, 0, (QM_DEBFP, "ERROR: endless loop in qm_rdibdb\n"));
if (sm_is_success(ret))
ret = sm_error_perm(SM_EM_IBDB,
SM_E_UNEXPECTED);
}
else
{
once = true;
goto again;
}
}
#if SM_TEST_PRT
sm_io_fprintf(smioout, "#TAs: %u\n", ri_ctx->rix_ntas);
sm_io_fprintf(smioout, "#RCPTs: %u\n", ri_ctx->rix_nrcpts);
sm_io_flush(smioout);
#endif
qm_ri_ctx_free(ri_ctx);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1