/* * Copyright (c) 2002-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-idbr-0.c,v 1.49 2007/06/18 04:42:31 ca Exp $") #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 "sm/io.h" /* ** Test program to read content of IBDB. ** Can display the entire content or just the open transactions. ** Performs several consistency checks. */ #define FNAME "ibd" #define IBDBSIZE 8192 #define MAX_STR_SZ 256 #define INIT_SEQ 1 #define BHTSIZE (32 * 1024) #define IDB_DONE 'D' #define IDB_CANCEL 'C' #define IDB_OOPS 'X' static int Verbose; static int init_seq = INIT_SEQ; static int bhtsize = BHTSIZE; static int bhtmax = (BHTSIZE * 2); /* ** Note: this program doesn't free() data since it may use it later on ** for various Verbose output. ** A real "recovery" program would free transaction/recipient ** structures as soon as it figured out that the transaction/recipient ** have been completed (delivered/cancelled/transferred to DEFEDB). */ /* ** 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(bht_entry_P info, void *ctx) { ibdb_rcpt_P rcpt; sessta_id_P ta_id; SM_TEST(info != NULL); SM_TEST(ctx != NULL); rcpt = (ibdb_rcpt_P) info->bhe_value; SM_TEST(rcpt != NULL); SM_TEST(rcpt->ibr_ta_id != NULL); ta_id = (sessta_id_P) ctx; if (SESSTA_EQ(rcpt->ibr_ta_id, ta_id)) { if (Verbose > 2) { sm_io_fprintf(smioout, "cancel TA=%s ", ta_id); sm_io_fprintf(smioout, "rcpt_pa=%N, ", rcpt->ibr_pa); sm_io_fprintf(smioout, "rcpt_idx=%d, ", rcpt->ibr_idx); sm_io_fprintf(smioout, "\n"); } rcpt->ibr_ta_id[0] = IDB_CANCEL; } return SM_SUCCESS; } /* ** CANCEL_TA -- Cancel a transaction ** ** Parameters: ** bhr -- bhtable (with recipients) ** ta_id -- TA ID ** ** Returns: ** usual sm_error code */ static sm_ret_T cancel_ta(bht_P bhr, sessta_id_P ta_id) { if (Verbose > 2) sm_io_fprintf(smioout, "cancel TA=%s\n", ta_id); bht_walk(bhr, ta_cancel, (void *) ta_id); return SM_SUCCESS; } /* ** TA_ACTION -- Show status of transaction (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- unused ** ** Returns: ** usual sm_error code */ /* ARGSUSED1 */ static sm_ret_T ta_action(bht_entry_P info, void *ctx) { ibdb_ta_P ta; bool printit; char c; (void) ctx; SM_TEST(info != NULL); ta = (ibdb_ta_P) info->bhe_value; SM_TEST(ta != NULL); printit = false; SM_TEST(ta->ibt_ta_id != NULL); if (ta->ibt_ta_id == NULL) return -1; c = ta->ibt_ta_id[0]; if (c != IDB_DONE && c != IDB_CANCEL) { sm_io_fprintf(smioout, "MAIL: OPEN: "); printit = true; } else if (Verbose > 1) { sm_io_fprintf(smioout, "MAIL: "); if (c == IDB_DONE) sm_io_fprintf(smioout, "DONE: "); else if (c == IDB_CANCEL) sm_io_fprintf(smioout, "CANCEL: "); else sm_io_fprintf(smioout, "OOPS: "); printit = true; } if (printit) { sm_io_fprintf(smioout, "ta=%s, ", ta->ibt_ta_id); sm_io_fprintf(smioout, "cdb=%C, ", ta->ibt_cdb_id); sm_io_fprintf(smioout, "mail_pa=%N, ", ta->ibt_mail_pa); if (Verbose > 3) sm_io_fprintf(smioout, "nrcpts=%d, ", ta->ibt_nrcpts); sm_io_fprintf(smioout, "\n"); } return SM_SUCCESS; } /* ** RCPT_ACTION -- Show status of recipient (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- unused ** ** Returns: ** usual sm_error code */ /* ARGSUSED1 */ static sm_ret_T rcpt_action(bht_entry_P info, void *ctx) { ibdb_rcpt_P rcpt; bool printit; char c; (void) ctx; SM_TEST(info != NULL); rcpt = (ibdb_rcpt_P) info->bhe_value; SM_TEST(rcpt != NULL); printit = false; SM_TEST(rcpt->ibr_ta_id != NULL); if (rcpt->ibr_ta_id == NULL) return -1; c = rcpt->ibr_ta_id[0]; if (c != IDB_DONE && c != IDB_CANCEL) { sm_io_fprintf(smioout, "RCPT: OPEN: "); printit = true; } else if (Verbose > 1) { sm_io_fprintf(smioout, "RCPT: "); if (c == IDB_DONE) sm_io_fprintf(smioout, "DONE: "); else if (c == IDB_CANCEL) sm_io_fprintf(smioout, "CANCEL: "); else sm_io_fprintf(smioout, "OOPS: "); printit = true; } if (printit) { sm_io_fprintf(smioout, "ta=%s, ", rcpt->ibr_ta_id); sm_io_fprintf(smioout, "rcpt_pa=%N, ", rcpt->ibr_pa); sm_io_fprintf(smioout, "rcpt_idx=%d, ", rcpt->ibr_idx); sm_io_fprintf(smioout, "\n"); } return SM_SUCCESS; } /* ** TESTIDBR -- read INCEDB records ** ** Parameters: ** maxs -- maximum size of an INCEDB file ** ** Returns: ** usual sm_error code. */ static sm_ret_T testidbr(int maxs) { int nrcpts, ntas, status; ibdbr_ctx_P ibdbrc; ibdb_ta_P ta, lta; ibdb_rcpt_P rcpt, lrcpt; ibdb_hdrmod_P ibdb_hdrmod; bht_P bht, bhr; bht_entry_P bhte; id_count_T id_count; sm_str_P str; sm_ret_T ret; bool more; rcpt_id_P rcpt_id; bhr = NULL; ibdbrc = NULL; ret = sm_error_temp(SM_EM_IBDB, ENOMEM); bht = bht_new(bhtsize, bhtmax); SM_TEST(bht != NULL); if (bht == NULL) return sm_error_temp(SM_EM_IBDB, ENOMEM); bhr = bht_new(bhtsize, bhtmax); SM_TEST(bhr != NULL); if (bhr == NULL) goto end; ret = ibdbr_open(NULL, FNAME, SM_IO_RDONLY, init_seq, maxs, IBDB_OFL_WRITE, &ibdbrc); if (sm_is_err(ret) && sm_error_value(ret) == ENOENT) goto end; SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(ibdbrc != NULL); if (ibdbrc == NULL) goto end; ret = ibdbr_ta_new(&ta); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(ta != NULL); if (ta == NULL) goto end; ret = ibdbr_rcpt_new(&rcpt); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(rcpt != NULL); if (rcpt == NULL) goto end; ret = ibdbr_hdrmod_new(&ibdb_hdrmod); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(ibdb_hdrmod != NULL); if (ibdb_hdrmod == NULL) goto end; str = sm_str_new(NULL, MAX_STR_SZ, MAX_STR_SZ); SM_TEST(str != NULL); if (str == NULL) goto end; more = true; nrcpts = ntas = 0; id_count = 0; do { id_count++; sm_str_clr(str); ret = ibdbr_get(ibdbrc, rcpt, ta, ibdb_hdrmod, &status); switch (ret) { case RT_IBDB_TA: if (Verbose > 2) { sm_io_fprintf(smioout, "MAIL: "); sm_io_fprintf(smioout, "ta=%s, ", ta->ibt_ta_id); sm_io_fprintf(smioout, "cdb=%C, ", ta->ibt_cdb_id); sm_io_fprintf(smioout, "mail_pa=%N, ", ta->ibt_mail_pa); sm_io_fprintf(smioout, "status=%d, ", status); if (Verbose > 3) sm_io_fprintf(smioout, "nrcpts=%d, ", ta->ibt_nrcpts); sm_io_fprintf(smioout, "\n"); } ++ntas; if (status == 0 || status == IBDB_TA_CANCEL) { if (status == IBDB_TA_CANCEL) { cancel_ta(bhr, ta->ibt_ta_id); ta->ibt_ta_id[0] = IDB_CANCEL; } ret = bht_add(bht, ta->ibt_ta_id, SMTP_STID_SIZE, ta, &bhte); SM_TEST(ret == SM_SUCCESS); SM_TEST(bhte != NULL); ret = ibdbr_ta_new(&ta); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(ta != NULL); if (ta == NULL) goto end; } else { lta = bht_find(bht, ta->ibt_ta_id, SMTP_STID_SIZE); SM_TEST(lta != NULL || idbd_ta_done(status)); if (lta != NULL) { lta->ibt_ta_id[0] = idbd_ta_done(status) ? IDB_DONE : (idbd_ta_cancelled(status) ? IDB_CANCEL : IDB_OOPS); } else if (idbd_ta_done(status)) { if (Verbose > 4) sm_io_fprintf(smioout, "INFO: can't find ta %s, status=%x\n", ta->ibt_ta_id, status); } else if (Verbose > 0) sm_io_fprintf(smioout, "ERROR: can't find ta %s\n", ta->ibt_ta_id); SM_CSTR_FREE(ta->ibt_cdb_id); } break; case RT_IBDB_RCPT: if (Verbose > 2) { sm_io_fprintf(smioout, "RCPT: "); sm_io_fprintf(smioout, "ta=%s, ", rcpt->ibr_ta_id); sm_io_fprintf(smioout, "rcpt_pa=%N, ", rcpt->ibr_pa); sm_io_fprintf(smioout, "rcpt_idx=%d, ", rcpt->ibr_idx); sm_io_fprintf(smioout, "status=%d, ", status); sm_io_fprintf(smioout, "\n"); } ++nrcpts; if (status == 0) { rcpt_id = (char *) sm_malloc(SMTP_RCPTID_SIZE + 1); SM_TEST(rcpt_id != NULL); if (rcpt_id == NULL) goto end; sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, rcpt->ibr_ta_id, rcpt->ibr_idx); ret = bht_add(bhr, rcpt_id, SMTP_RCPTID_SIZE, rcpt, &bhte); SM_TEST(ret == SM_SUCCESS); SM_TEST(bhte != NULL); ret = ibdbr_rcpt_new(&rcpt); SM_TEST(ret == SM_SUCCESS); if (ret != SM_SUCCESS) return ret; SM_TEST(rcpt != NULL); if (rcpt == NULL) goto end; } else { rcpt_id_T lrcpt_id; sm_snprintf(lrcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, rcpt->ibr_ta_id, rcpt->ibr_idx); lrcpt = bht_find(bhr, lrcpt_id, SMTP_RCPTID_SIZE); SM_TEST(lrcpt != NULL || idbd_rcpt_done(status)); if (lrcpt != NULL) { lrcpt->ibr_ta_id[0] = idbd_rcpt_done(status) ? IDB_DONE : IDB_OOPS; } else if (idbd_rcpt_done(status)) { if (Verbose > 4) sm_io_fprintf(smioout, "INFO: can't find rcpt %s, status=%x\n", lrcpt_id, status); } else if (Verbose > 0) sm_io_fprintf(smioout, "ERROR: can't find rcpt %s\n", lrcpt_id); } break; case RT_IBDB_HDRMOD: if (Verbose > 2) { sm_io_fprintf(smioout, "HDRMOD: "); sm_io_fprintf(smioout, "ta=%s, ", ibdb_hdrmod->ibh_ta_id); sm_io_fprintf(smioout, "hdr=%T, ", ibdb_hdrmod->ibh_hdr); sm_io_fprintf(smioout, "type=%u, ", ibdb_hdrmod->ibh_type); sm_io_fprintf(smioout, "pos=%u", ibdb_hdrmod->ibh_pos); sm_io_fprintf(smioout, "\n"); } break; default: if (sm_is_err(ret) && sm_error_value(ret) == ENOENT) sm_io_fprintf(smioout, "end of ibd\n"); else sm_io_fprintf(smioout, "ERROR: ibdbr_get()=%x\n", ret); more = false; break; } } while (more); bht_walk(bht, ta_action, NULL); bht_walk(bhr, rcpt_action, NULL); end: if (ibdbrc != NULL) { ret = ibdbr_close(ibdbrc); SM_TEST(ret == SM_SUCCESS); } if (bht != NULL) bht_destroy(bht, NULL, NULL); if (bhr != NULL) bht_destroy(bhr, NULL, NULL); return ret; } static void usage(const char *prg) { sm_io_fprintf(smioerr, "usage: %s [options]\n", prg); sm_io_fprintf(smioerr, "-d n set verbose level\n"); sm_io_fprintf(smioerr, "-I n initialize IBDB file number\n"); sm_io_fprintf(smioerr, "-m n set IBDB file size\n"); sm_io_fprintf(smioerr, "-M n maximum hash table size [%d]\n", bhtmax); sm_io_fprintf(smioerr, "-T n hash table size [%d]\n", bhtsize); sm_io_fprintf(smioerr, "-V increase verbose level\n"); exit(1); } int main(int argc, char *argv[]) { int c, maxs; sm_ret_T ret; Verbose = 0; maxs = IBDBSIZE; while ((c = getopt(argc, argv, "d:HI:m:M:T:V")) != -1) { switch (c) { case 'd': Verbose = strtol(optarg, NULL, 0); break; #if SM_HEAP_CHECK case 'H': SmHeapCheck = atoi(optarg); break; #endif case 'I': init_seq = atoi(optarg); break; case 'm': maxs = strtol(optarg, NULL, 0); break; case 'M': bhtmax = strtol(optarg, NULL, 0); break; case 'T': bhtsize = strtol(optarg, NULL, 0); break; case 'V': ++Verbose; break; default: usage(argv[0]); return(1); } } sm_test_begin(argc, argv, "test sm_idbr_0"); ret = testidbr(maxs); sm_io_flush(smioout); return sm_test_end(); }