/* * 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-idbrw-3.c,v 1.37 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" /* ** Test program to write IBDB and then recover the content of IBDB. ** Performs consistency checks (whether all open transactions are found). ** Uses "wribdb.c" to write IBDB. ** Performs a recovery run using ibdbrcvr_open() from "../qmgr/rdibdb.c" */ static int debug = 0; static int id_mod1 = 10; static int id_mod2 = 4; static int id_modta = 0; static int rcpt_mod = 2; #define NTAS 100 #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; /* test context */ struct te_ctx_S { /* ** These two hash tables contain the open TAs/RCPTs after ** IBDB has been written. */ bht_P tex_crt_bht; /* hash table for TAs created */ bht_P tex_crt_bhr; /* hash table for RCPTs created */ /* ** These two hash tables contain the open TAs/RCPTs after ** IBDB has been read by rdibdb(); i.e., they point to ** the hash tables in ri_ctx. ** ** After IBDB has been written and recovered, all entries that ** are in the "crt" hash tables must be in the "rd" hash tables too. */ bht_P tex_rd_bht; /* hash table for TAs read */ bht_P tex_rd_bhr; /* hash table for RPCTs read */ uint tex_ntas; /* number of TAs */ uint tex_nrcpts; /* number of RCPTs */ }; /* XXX 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 DONTWR /* ** DONTWR -- Decides whether to write a RCPT/TA record ** ** Parameters: ** ntas -- number of transactions ** id_count -- id ** rcpt_idx -- rcpt index ** isrcpt -- is rcpt? ** ** Returns: ** true iff don't "close" TA/RCPT */ static bool dontwr(int ntas, id_count_T id_count, int rcpt_idx, bool isrcpt) { if (id_count >= ntas) return false; if ((id_count % id_mod1) == 0) return true; if ((id_count % id_mod2) == 0 && ((rcpt_idx % rcpt_mod) == 0 || !isrcpt)) return true; return false; } #define TACANCEL /* ** TACANCEL -- Decides whether to cancel a TA record ** ** Parameters: ** ntas -- number of transactions ** id_count -- id ** ** Returns: ** true iff don't "close" TA/RCPT */ static bool tacancel(int ntas, id_count_T id_count) { if (id_count >= ntas) return false; if (id_modta != 0 && (id_count % id_modta) == 0) return true; return false; } #define FNAME "ibd" #define IBDBSIZE 8192 #define INIT_SEQ 1 static int init_seq = INIT_SEQ; #include "wribdb.c" 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)) 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); SM_TEST(ret == SM_SUCCESS); 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) { res = ibdbr_close(ibdbrc); /* XXX Complain on error? */ ibdbrc = NULL; } res = ibdbrcvr_close(); /* XXX check result? */ 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) { 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; } /* ** TEST_RCPT_ACTION -- Check whether recipient is in read ht (cb fct) ** ** Parameters: ** info -- bht entry ** ctx -- context ** ** Returns: ** usual sm_error code */ static sm_ret_T test_rcpt_action(bht_entry_P info, void *ctx) { 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-%04d\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; } /* ** TESTIDBWRRD -- write/read IBDB ** ** Parameters: ** rdonly -- read only (don't write ibdb) ** ntas -- number of transactions ** maxs -- maximum size of an INCEDB file ** random -- create random IBDB "errors" (omissions) ** ** Returns: ** usual sm_error code. */ static sm_ret_T testidbwrrd(bool rdonly, int ntas, int maxs, bool random) { 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; if (!rdonly) { ret = testidbwr(NULL, ntas, maxs, random ? WRIBDB_FL_RANDOM : 0, bht, bhr); SM_TEST(ret == SM_SUCCESS); } /* XXX HACK */ ret = rdibdb(ri_ctx); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) goto error; /* check recipients first to count them for TAs */ ret = bht_walk(bhr, test_rcpt_action, &te_ctx); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS && debug > 3) goto error; /* Now run through open transactions */ ret = bht_walk(bht, test_ta_action, &te_ctx); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS && debug > 3) goto error; #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); } #endif /* SM_TEST_PRT */ sm_io_flush(smioout); /* 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: sm_io_flush(smioout); return ret; } static void usage(const char *prg) { fprintf(stderr, "usage: %s [options]\n", prg); fprintf(stderr, "-A n omit every n'th transaction/recipient\n"); fprintf(stderr, "-d n set debug level\n"); fprintf(stderr, "-i n n iterations [%d]\n", NTAS); fprintf(stderr, "-m n maximum size of IBDB file [%d]\n", IBDBSIZE); fprintf(stderr, "-M n omit every n'th transaction\n"); fprintf(stderr, "-q n set qm_debug level\n"); fprintf(stderr, "-R n omit every n'th recipient\n"); fprintf(stderr, "-w do not write IBDB, read existing files\n"); exit(1); } int main(int argc, char *argv[]) { int ntas, c, maxs; sm_ret_T ret; bool random, rdonly; debug = 0; ntas = NTAS; maxs = IBDBSIZE; random = false; rdonly = false; while ((c = getopt(argc, argv, "A:C:d:i:I:m:M:q:R:rT:w")) != -1) { switch (c) { case 'A': id_mod1 = strtol(optarg, NULL, 0); if (id_mod1 <= 1) return 1; break; case 'C': rand_mod = strtol(optarg, NULL, 0); if (rand_mod <= 1) return 1; break; case 'd': debug = strtoul(optarg, NULL, 0); break; case 'i': ntas = strtol(optarg, NULL, 0); if (ntas <= 0) return 1; break; case 'I': init_seq = atoi(optarg); break; case 'm': maxs = strtol(optarg, NULL, 0); if (maxs <= 0) return 1; break; case 'M': id_mod2 = strtol(optarg, NULL, 0); if (id_mod2 <= 1) return 1; break; case 'q': qm_debug[0] = strtoul(optarg, NULL, 0); break; case 'R': rcpt_mod = strtol(optarg, NULL, 0); if (rcpt_mod <= 1) return 1; break; case 'r': random = true; break; case 'T': id_modta = strtol(optarg, NULL, 0); if (id_modta <= 1) return 1; break; case 'w': rdonly = true; break; default: usage(argv[0]); return 1; } } sm_test_begin(argc, argv, "test sm_idbr_3"); ret = testidbwrrd(rdonly, ntas, maxs, random); return sm_test_end(); }