/*
* 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();
}
syntax highlighted by Code2HTML, v. 0.9.1