/* * 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_IDSTR(id, "@(#)$Id: t-idbrecover-2.c,v 1.28 2007/06/18 04:42:30 ca Exp $") #define QMGR_DEBUG_DEFINE 1 #include "sm/io.h" #include "sm/mta.h" #include "sm/ibdb.h" #include "sm/cdb.h" #include "sm/qmgr.h" #include "sm/memops.h" #include "sm/bhtable.h" #include "sm/test.h" #include /* ** Test program to recover content of IBDB. ** Performs a recovery run using ibdbrcvr_open() from "../qmgr/rdibdb.c" */ static int debug = 0; #define SM_TEST_PRT 1 /* context for *_action() calls (and probably others later on) */ typedef struct te_ctx_S te_ctx_T, *te_ctx_P; struct te_ctx_S { bht_P tex_crt_bht; bht_P tex_crt_bhr; bht_P tex_rd_bht; bht_P tex_rd_bhr; uint tex_ntas; uint tex_nrcpts; }; /* HACK */ #include "../qmgr/rdibdb.c" /* dummy function */ sm_ret_T edb_reql_free(edb_ctx_P edb_ctx, edb_req_hd_P edb_req_hd) { return SM_SUCCESS; } #define FNAME "ibd" #define IBDBSIZE 8192 #define INIT_SEQ 1 static int init_seq = INIT_SEQ; static sm_ret_T rdibdb(ri_ctx_P ri_ctx) { uint32_t first, last; sm_ret_T ret, ibdb_which, res; bool once; ibdbr_ctx_P ibdbrc; /* Initialize most variables */ ibdbrc = NULL; ibdb_which = 0; once = false; ret = SM_SUCCESS; again: /* Prepare for recovery */ ibdb_which = ibdbrcvr_open(); QM_LEV_DPRINTF(4, (QM_DEBFP, "qm_rdibdb running; once=%d, ibdbrcvr_open=%x\n", once, ibdb_which)); if (sm_is_err(ibdb_which)) { SM_TEST(ibdb_which == SM_SUCCESS); goto end; } if (ibdb_which == SM_IBDB_NO_RCVR) goto end; ret = ibdbf_get_seq(NULL, IBDB_NAME, IBDB_OFL_RCVR, &first, &last); QM_LEV_DPRINTF(4, (QM_DEBFP, "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(NULL, IBDB_NAME, SM_IO_RDONLY, first, 0 /* unused */, IBDB_OFL_RCVR, &ibdbrc); if (ret != SM_SUCCESS) goto end; ri_ctx->rix_ibdbrc = ibdbrc; ri_ctx->rix_time = time(NULLT); /* Actually read IBDB */ ret = qm_ribdb(ri_ctx); if (sm_is_err(ret)) goto end; if (first <= last && ret == SM_SUCCESS) { res = ibdbr_unlink(ibdbrc, first, last); QM_LEV_DPRINTF(2, (QM_DEBFP, "qm_rdibdb: ibdbr_unlink=%x\n", ret)); } end: if (ibdbrc != NULL) { ret = ibdbr_close(ibdbrc); /* XXX Complain on error? */ ibdbrc = NULL; } res = ibdbrcvr_close(); /* check result? */ SM_TEST(res == SM_SUCCESS); QM_LEV_DPRINTF(3, (QM_DEBFP, "qm_rdibdb: ibdbrcvr_close=%x\n", ret)); if (ibdb_which == SM_IBDB_RCVR_EXISTS) { if (once) { /* oops ... been there, done that? */ QM_LEV_DPRINTF(0, (QM_DEBFP, "ERROR: endless loop in qm_rdibdb\n")); } else { once = true; goto again; } } #if SM_TEST_PRT if (debug > 1) { 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 /* SM_TEST_PRT */ return ret; } /* ** TA_ACTION -- Add transaction to DEFEDB (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- context ** ** Returns: ** usual sm_error code */ static sm_ret_T test_ta_action(bht_entry_P info, void *ctx) { sm_ret_T ret; ibdb_ta_P ibdb_ta, ibdb_ta2; te_ctx_P te_ctx; te_ctx = (te_ctx_P) ctx; ibdb_ta = (ibdb_ta_P) info->bhe_value; if (ibdb_ta->ibt_ta_id == NULL) return sm_err_perm(EINVAL); ++te_ctx->tex_ntas; if (debug > 2) sm_io_fprintf(smioout, "OPEN TA: ta=%s\n", info->bhe_key); ibdb_ta2 = bht_find(te_ctx->tex_rd_bht, info->bhe_key, SMTP_STID_SIZE); SM_TEST(ibdb_ta2 != NULL); if (ibdb_ta2 == NULL) { /* OOPS what's going on? Where's our TA? */ sm_io_fprintf(smioout, "ERROR: bht_find(%s) failed\n", info->bhe_key); return sm_err_perm(ENOENT); } #if 0 sm_io_fprintf(smioout, "OPEN 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, ", ibdb_ta->ibt_mail_pa); sm_io_fprintf(smioout, "\n"); #endif /* SM_TEST_PRT */ return SM_SUCCESS; } /* ** RCPT_ACTION -- Add recipient to DEFEDB (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- context ** ** Returns: ** usual sm_error code */ static sm_ret_T test_rcpt_action(bht_entry_P info, void *ctx) { sm_ret_T ret; ibdb_rcpt_P ibdb_rcpt, ibdb_rcpt2; te_ctx_P te_ctx; te_ctx = (te_ctx_P) ctx; ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value; if (ibdb_rcpt->ibr_ta_id == NULL) return sm_err_perm(EINVAL); ++te_ctx->tex_nrcpts; if (debug > 2) sm_io_fprintf(smioout, "OPEN RCPT: ta=%s-%d\n", ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx); ibdb_rcpt2 = bht_find(te_ctx->tex_rd_bhr, info->bhe_key, SMTP_RCPTID_SIZE); SM_TEST(ibdb_rcpt2 != NULL); if (ibdb_rcpt2 == NULL) { /* OOPS what's going on? Where's our rcpt? */ sm_io_fprintf(smioout, "ERROR: bht_find(%s) failed\n", info->bhe_key); return sm_err_perm(ENOENT); } #if 0 sm_io_fprintf(smioout, "rcpt_pa=%S, ", ibdb_rcpt->ibr_pa); sm_io_fprintf(smioout, "rcpt_idx=%d, ", ibdb_rcpt->ibr_idx); sm_io_fprintf(smioout, "\n"); #endif /* SM_TEST_PRT */ return SM_SUCCESS; } /* ** TESTIDB -- read IBDB ** ** Parameters: ** None ** ** Returns: ** usual sm_error code. */ static sm_ret_T testidbr(void) { sm_ret_T ret; ri_ctx_P ri_ctx; bht_P bht, bhr; te_ctx_T te_ctx; bht = NULL; bhr = NULL; ret = sm_err_temp(ENOMEM); bht = bht_new(BHTSIZE, BHTSIZE); if (bht == NULL) goto error; bhr = bht_new(BHTSIZE, BHTSIZE); if (bhr == NULL) goto error; te_ctx.tex_crt_bht = bht; te_ctx.tex_crt_bhr = bhr; te_ctx.tex_ntas = 0; te_ctx.tex_nrcpts = 0; ret = qm_ri_ctx_new(NULL, &ri_ctx); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; te_ctx.tex_rd_bht = ri_ctx->rix_bht_ta; te_ctx.tex_rd_bhr = ri_ctx->rix_bht_rcpt; /* HACK */ ret = rdibdb(ri_ctx); SM_TEST(ret == SM_SUCCESS); /* check recipients first to count them for TAs */ bht_walk(bhr, test_rcpt_action, &te_ctx); /* Now run through open transactions */ bht_walk(bht, test_ta_action, &te_ctx); #if 0 /* don't match... open != all */ SM_TEST(ri_ctx->rix_ntas == te_ctx.tex_ntas ); SM_TEST(ri_ctx->rix_nrcpts == te_ctx.tex_nrcpts); #endif /* 0 */ #if SM_TEST_PRT if (debug > 1) { sm_io_fprintf(smioout, "C#TAs: %u\n", te_ctx.tex_ntas); sm_io_fprintf(smioout, "C#RCPTs: %u\n", te_ctx.tex_nrcpts); sm_io_flush(smioout); } #endif /* SM_TEST_PRT */ /* HACK to avoid free... */ ri_ctx->rix_bht_ta = NULL; ri_ctx->rix_bht_rcpt = NULL; ret = qm_ri_ctx_free(ri_ctx); SM_TEST(ret == SM_SUCCESS); return ret; error: return ret; } static void usage(const char *prg) { fprintf(stderr, "usage: %s [options]\n", prg); fprintf(stderr, "-d n set debug level\n"); fprintf(stderr, "-I n initialize IBDB file number\n"); exit(1); } int main(int argc, char *argv[]) { int c; sm_ret_T ret; debug = 0; while ((c = getopt(argc, argv, "d:I:")) != -1) { switch (c) { case 'd': debug = strtoul(optarg, NULL, 0); break; case 'I': init_seq = atoi(optarg); break; default: usage(argv[0]); return 1; } } sm_test_begin(argc, argv, "test sm_idbr_2"); ret = testidbr(); return sm_test_end(); }