/*
* 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: t-edbr-1.c,v 1.38 2007/06/18 04:42:30 ca Exp $")
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/test.h"
#include "sm/bhtable.h"
#include "sm/edb.h"
#include "sm/actdb-int.h"
#include "sm/regex.h"
#define QMGR_DEBUG_DEFINE 1
#include "sm/qmgrdbg.h"
/*
** Print and check content of DEFEDB.
** This is almost a "mailq" command.
*/
/* Hack ... */
#define RCPT_MAX_LEN 256
#define MAX_STRLEN 1024
#define ERRBUF_SIZE 1024
#define MAX_MATCH 256
#define T_BHT_SIZE 1023
#define TA_TYPE 'T'
#define RCPT_TYPE 'R'
#define BOTH_TYPE 'B' /* seen both entries */
static int Verbose;
static regex_t IdReg;
static regex_t AddrReg;
static bool IdPat;
static bool AddrPat;
static int
b_regmatch(const regex_t *preg, const char *string, int eflags)
{
int r;
size_t nmatch;
regmatch_t pmatch[MAX_MATCH];
nmatch = 0;
r = regexec(preg, string, nmatch, pmatch, eflags);
return r;
}
static aq_rcpt_P
new_rcpt(void)
{
aq_rcpt_P aq_rcpt;
aq_rcpt = (aq_rcpt_P) sm_zalloc(sizeof(*aq_rcpt));
SM_TEST(aq_rcpt != NULL);
if (aq_rcpt == NULL)
return NULL;
#if AQ_RCPT_CHECK
aq_rcpt->sm_magic = SM_AQ_RCPT_MAGIC;
#endif /* AQ_TA_CHECK */
return aq_rcpt;
}
static sm_ret_T
aq_rcpt_clr(aq_rcpt_P aq_rcpt)
{
if (aq_rcpt == NULL)
return SM_SUCCESS;
if (aq_rcpt->aqr_addrs != NULL
&& !AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR)
&& aq_rcpt->aqr_addrs != &(aq_rcpt->aqr_addr_mf))
SM_FREE(aq_rcpt->aqr_addrs);
SM_STR_FREE(aq_rcpt->aqr_pa);
SM_STR_FREE(aq_rcpt->aqr_msg);
SM_STR_FREE(aq_rcpt->aqr_dsn_msg);
SM_FREE(aq_rcpt->aqr_dsns);
/* clear entire struct? */
aq_rcpt->aqr_err_st = 0;
aq_rcpt->aqr_addr_fail = 0;
aq_rcpt->aqr_dsn_idx = 0;
return SM_SUCCESS;
}
static void
printtime(time_T t, char *name)
{
if (Verbose > 1)
sm_io_fprintf(smioout, "\t%s=%ld %s", name, (long) t,
ctime(&t));
else
sm_io_fprintf(smioout, "\t%s=%ld\n", name, (long) t);
}
static aq_ta_P
new_ta(void)
{
aq_ta_P aq_ta;
aq_ta = (aq_ta_P) sm_zalloc(sizeof(*aq_ta));
SM_TEST(aq_ta != NULL);
if (aq_ta == NULL)
return NULL;
aq_ta->aqt_mail = (aq_mail_P) sm_zalloc(sizeof(*(aq_ta->aqt_mail)));
SM_TEST(aq_ta->aqt_mail != NULL);
if (aq_ta->aqt_mail == NULL)
return NULL;
#if AQ_TA_CHECK
aq_ta->sm_magic = SM_AQ_TA_MAGIC;
#endif /* AQ_TA_CHECK */
return aq_ta;
}
/* ARGSUSED1 */
static void
free_fn(void *str, void *key, void *ctx)
{
(void) key;
(void) ctx;
if (str != NULL)
sm_free(str);
}
/* ARGSUSED0 */
static void
free_aqs(void *aq, void *key, void *ctx)
{
/* memory leak ... */
/* should free aq */
(void) aq;
(void) key;
(void) ctx;
}
/* ARGSUSED1 */
static sm_ret_T
walk_fn(bht_entry_P be, void *ctx)
{
char *type;
(void) ctx;
type = (char *) be->bhe_value;
SM_TEST(type != NULL);
if (type == NULL)
return sm_err_perm(ENOENT);
SM_TEST(*type == BOTH_TYPE);
if (*type != BOTH_TYPE)
{
if (Verbose > 0)
sm_io_fprintf(smioerr, "type=%c, id=%s\n",
*type, be->bhe_key);
return sm_err_perm(ENOENT);
}
return SM_SUCCESS;
}
/* ARGSUSED1 */
static sm_ret_T
walk_ta(bht_entry_P be, void *ctx)
{
aq_ta_P aq_ta;
(void) ctx;
aq_ta = (aq_ta_P) be->bhe_value;
SM_TEST(aq_ta != NULL);
if (aq_ta == NULL)
return sm_err_perm(ENOENT);
SM_TEST(aq_ta->aqt_rcpts_inaq == 0);
if (aq_ta->aqt_rcpts_inaq != 0)
{
if (Verbose > 0)
sm_io_fprintf(smioerr, "ta=%s, rcpts_missing=%d\n",
aq_ta->aqt_ss_ta_id,
aq_ta->aqt_rcpts_inaq);
}
return SM_SUCCESS;
}
/* add some logic to check counters, e.g., tried, total, left, ... */
static bool
lookup(bht_P tn, char *id, size_t len, char type)
{
sm_ret_T ret;
char *str, *str2;
bht_entry_P bi;
str = bht_find(tn, id, len);
if (str == NULL)
{
str = sm_malloc(len);
SM_TEST(str != NULL);
if (str == NULL)
return false;
str2 = sm_malloc(1);
SM_TEST(str2 != NULL);
if (str2 == NULL)
return false;
*str2 = type;
ret = bht_add(tn, id, len, str2, &bi);
SM_TEST(ret == SM_SUCCESS);
SM_TEST(bi != NULL);
}
else
{
if (type == TA_TYPE)
{
/* There can be only one.... transaction */
if (*str == type)
{
SM_TEST(*str != type);
return false;
}
}
/* If it's a different type (TA/RCPT) then set it to BOTH */
if (*str != type)
*str = BOTH_TYPE;
}
return true;
}
static void
edbr1(uint flags)
{
sm_ret_T ret, rt;
uint u;
bool ok;
edb_ctx_P edb_ctx;
edb_cnf_T edb_cnf;
aq_ctx_P aq_ctx;
aq_ctx_T aq_ctx_s;
aq_rcpt_P aq_rcpt;
aq_ta_P aq_ta, aq_ta_f;
edb_req_P edb_req;
edb_cursor_P edb_cursor;
bht_P tn, t_aq;
bht_entry_P bi;
aq_rcpt = NULL;
aq_ta = NULL;
edb_req = NULL;
tn = NULL;
t_aq = NULL;
edb_cursor = NULL;
edb_ctx = NULL;
aq_ctx = &aq_ctx_s;
sm_memzero(&edb_cnf, sizeof(edb_cnf));
edb_cnf.edbcnf_oflags = flags;
ret = edb_open(&edb_cnf, NULL, NULL, &edb_ctx);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
{
if (Verbose > 0)
sm_io_fprintf(smioerr, "edb_open=%r\n", ret);
return;
}
ret = aq_open(NULL, &aq_ctx, 256, 0);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
aq_rcpt = new_rcpt();
SM_TEST(aq_rcpt != NULL);
if (aq_rcpt == NULL)
goto error;
ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, THR_NO_LOCK);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
tn = bht_new(T_BHT_SIZE, T_BHT_SIZE * 2);
SM_TEST(tn != NULL);
if (tn == NULL)
goto error;
t_aq = bht_new(T_BHT_SIZE, T_BHT_SIZE * 2);
SM_TEST(t_aq != NULL);
if (t_aq == NULL)
goto error;
ret = edb_rd_open(edb_ctx, &edb_cursor);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
for (;;)
{
ret = edb_rd_next(edb_ctx, edb_cursor, edb_req);
if (ret == sm_error_perm(SM_EM_EDB, DB_NOTFOUND))
break;
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
rt = edb_get_type(edb_req);
if (sm_is_err(rt))
goto error;
if (rt == EDB_REQ_TA)
{
aq_ta = new_ta();
SM_TEST(aq_ta != NULL);
if (aq_ta == NULL)
goto error;
ret = edb_ta_dec(edb_req, aq_ta);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
aq_ta->aqt_ss_ta_id[SMTP_STID_SIZE - 1] = '\0';
if (IdPat &&
b_regmatch(&IdReg, aq_ta->aqt_ss_ta_id, 0) != 0)
continue;
if (AddrPat &&
b_regmatch(&AddrReg,
(const char *) sm_str_getdata(aq_ta->aqt_mail->aqm_pa), 0) != 0)
continue;
sm_io_fprintf(smioout, "got transaction\n");
sm_io_fprintf(smioout, "\tta_id=%s\n",
aq_ta->aqt_ss_ta_id);
sm_io_fprintf(smioout, "\tmail=%S\n",
aq_ta->aqt_mail->aqm_pa);
printtime(aq_ta->aqt_st_time, "time");
sm_io_fprintf(smioout, "\tcdb=%s\n",
sm_cstr_data(aq_ta->aqt_cdb_id));
sm_io_fprintf(smioout, "\trcpts_tot=%u\n",
aq_ta->aqt_rcpts_tot);
sm_io_fprintf(smioout, "\trcpts_left=%u\n",
aq_ta->aqt_rcpts_left);
sm_io_fprintf(smioout, "\trcpts_temp=%u\n",
aq_ta->aqt_rcpts_temp);
sm_io_fprintf(smioout, "\trcpts_perm=%u\n",
aq_ta->aqt_rcpts_perm);
if (Verbose > 0)
{
sm_io_fprintf(smioout, "\trcpts_tried=%u\n",
aq_ta->aqt_rcpts_tried);
sm_io_fprintf(smioout, "\tnxt_idx=%u\n",
aq_ta->aqt_nxt_idx);
}
sm_io_fprintf(smioout, "\tstate=%d\n",
aq_ta->aqt_state);
sm_io_fprintf(smioout, "\taqt_rcpts_ar=%d\n",
aq_ta->aqt_rcpts_ar);
sm_io_fprintf(smioout, "\taqt_owners_n=%d\n",
aq_ta->aqt_owners_n);
for (u = 0; u < aq_ta->aqt_owners_n; u++)
{
sm_io_fprintf(smioout, "\towner[%u]=%S\n",
u, aq_ta->aqt_owners_pa[u]);
}
/* for checks... */
aq_ta->aqt_rcpts_inaq = aq_ta->aqt_rcpts_left;
ok = lookup(tn, aq_ta->aqt_ss_ta_id,
sizeof(aq_ta->aqt_ss_ta_id), TA_TYPE);
if (!ok)
goto error;
ret = bht_add(t_aq, aq_ta->aqt_ss_ta_id,
sizeof(aq_ta->aqt_ss_ta_id), aq_ta, &bi);
SM_TEST(ret == SM_SUCCESS);
SM_TEST(bi != NULL);
}
else if (rt == EDB_REQ_RCPT)
{
aq_rcpt_clr(aq_rcpt);
ret = edb_rcpt_dec(edb_req, aq_rcpt);
SM_TEST(ret == SM_SUCCESS);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_ss_ta_id[SMTP_STID_SIZE - 1] = '\0';
if (IdPat &&
b_regmatch(&IdReg, aq_rcpt->aqr_ss_ta_id, 0) != 0)
continue;
if (AddrPat &&
b_regmatch(&AddrReg,
(const char *) sm_str_getdata(aq_rcpt->aqr_pa), 0) != 0)
continue;
sm_io_fprintf(smioout, "got recipient\n");
sm_io_fprintf(smioout, "\tta_id=%s\n",
aq_rcpt->aqr_ss_ta_id);
sm_io_fprintf(smioout, "\trcpt=%S\n",
aq_rcpt->aqr_pa);
sm_io_fprintf(smioout, "\taqr_rcpt_idx=%u\n",
aq_rcpt->aqr_idx);
if (Verbose > 0)
sm_io_fprintf(smioout, "\taqr_tries=%u\n",
aq_rcpt->aqr_tries);
if (Verbose > 0 && aq_rcpt->aqr_port != 0)
sm_io_fprintf(smioout, "\taqr_port=%hd\n",
aq_rcpt->aqr_port);
if (aq_rcpt->aqr_addr_max > 0)
{
if (Verbose > 0)
{
for (u = 0; u < aq_rcpt->aqr_addr_max;
++u)
{
sm_io_fprintf(smioout,
"\tsrv_ip4=%A\n",
aq_rcpt->aqr_addrs[u].aqra_ipv4);
printtime(aq_rcpt->aqr_addrs[u].aqra_expt,
"expt");
sm_io_fprintf(smioout,
"\tpref=%hu\n",
aq_rcpt->aqr_addrs[u].aqra_pref);
}
}
else
{
sm_io_fprintf(smioout, "\tsrv_ip4=%A\n",
aq_rcpt->aqr_addrs[0].aqra_ipv4);
}
}
else
sm_io_fprintf(smioout, "\tsrv_ip4=undef\n");
sm_io_fprintf(smioout, "\taqr_da_idx=%u\n",
aq_rcpt->aqr_da_idx);
printtime(aq_rcpt->aqr_st_time, "aqr_st_time ");
if (Verbose > 0)
{
printtime(aq_rcpt->aqr_last_try,
"aqr_last_try");
printtime(aq_rcpt->aqr_next_try,
"aqr_next_try");
}
sm_io_fprintf(smioout, "\taqr_status=%d\n",
aq_rcpt->aqr_status);
if (Verbose > 0)
{
sm_io_fprintf(smioout, "\tflags=0x%x\n",
aq_rcpt->aqr_flags);
}
if (Verbose > 0 && aq_rcpt->aqr_msg != NULL)
{
sm_io_fprintf(smioout, "\ttext=%S\n",
aq_rcpt->aqr_msg);
}
if (Verbose > 0 && aq_rcpt->aqr_dsn_msg != NULL)
{
sm_io_fprintf(smioout, "\tdsn_msg=%S\n",
aq_rcpt->aqr_dsn_msg);
}
if (Verbose > 0 && aq_rcpt->aqr_err_st != 0)
{
sm_io_fprintf(smioout, "\terr_st=0x%x\n",
aq_rcpt->aqr_err_st);
}
if (Verbose > 0 && aq_rcpt->aqr_addr_fail != 0)
{
sm_io_fprintf(smioout, "\thost=0x%x\n",
aq_rcpt->aqr_addr_fail);
}
if (Verbose > 0 && aq_rcpt_has_bounce(aq_rcpt))
{
sm_io_fprintf(smioout, "\tbounce_idx=%u\n",
aq_rcpt->aqr_dsn_idx);
}
if (Verbose > 0 && aq_rcpt_has_owner(aq_rcpt))
{
sm_io_fprintf(smioout, "\towner_idx=%u\n",
aq_rcpt->aqr_owner_idx);
}
#if MTA_USE_TLS
if (Verbose > 1 && aq_rcpt->aqr_conf != NULL) {
sm_io_fprintf(smioout, "\tconf=%S\n", aq_rcpt->aqr_conf);
sm_io_fprintf(smioout, "\tmaprescnf=%#x\n",
aq_rcpt->aqr_maprescnf);
}
#endif /* MTA_USE_TLS */
ok = lookup(tn, aq_rcpt->aqr_ss_ta_id,
sizeof(aq_rcpt->aqr_ss_ta_id), RCPT_TYPE);
if (!ok)
goto error;
aq_ta_f = bht_find(t_aq, aq_rcpt->aqr_ss_ta_id,
sizeof(aq_rcpt->aqr_ss_ta_id));
/* this fails if the rcpt is found before the TA */
SM_TEST(aq_ta_f != NULL);
if (aq_ta_f != NULL)
{
SM_TEST(aq_ta_f->aqt_rcpts_inaq > 0);
--aq_ta_f->aqt_rcpts_inaq;
}
}
else if (rt != EDB_REQ_VRS)
{
sm_io_fprintf(smioout, "got what? %x\n", rt);
SM_TEST(false);
if (sm_is_err(ret))
goto error;
}
}
bht_walk(t_aq, walk_ta, NULL);
bht_walk(tn, walk_fn, NULL);
error:
SM_FREE(aq_rcpt);
if (aq_ta != NULL)
{
SM_STR_FREE(aq_ta->aqt_mail->aqm_pa);
SM_FREE(aq_ta->aqt_mail);
SM_CSTR_FREE(aq_ta->aqt_cdb_id);
sm_free(aq_ta);
}
sm_io_flush(smioout);
if (edb_ctx != NULL && edb_cursor != NULL)
{
ret = edb_rd_close(edb_ctx, edb_cursor);
SM_TEST(ret == SM_SUCCESS);
}
if (aq_ctx != NULL)
{
ret = aq_close(aq_ctx);
SM_TEST(ret == SM_SUCCESS);
}
if (edb_ctx != NULL)
{
ret = edb_close(edb_ctx);
SM_TEST(ret == SM_SUCCESS);
}
if (tn != NULL)
bht_destroy(tn, free_fn, NULL);
if (t_aq != NULL)
bht_destroy(t_aq, free_aqs, NULL);
return;
}
static void
usage(const char *prg)
{
fprintf(stderr, "usage: %s [options]\n"
"Print content of main mail queue and"
" test it for consistency.\n"
"options:\n"
"-I regex: print only entries whose ID match regex\n"
"-R regex: print only entries whose address match regex\n"
"-V: increase verbosity (can be used multiple times)\n"
"-w: open DB environment read/write\n"
, prg);
exit(0);
}
static int
b_regcomp(regex_t *preg, const char *pattern, int pflags)
{
int regerr;
regerr = regcomp(preg, pattern, pflags);
if (regerr != 0)
{
/* Errorhandling */
char errbuf[ERRBUF_SIZE];
(void) regerror(regerr, preg, errbuf, sizeof errbuf);
fprintf(stderr, "pattern-compile-error: %s", errbuf);
}
return regerr;
}
int
main(int argc, char *argv[])
{
int r, c;
char *prg;
int regopts;
uint flags;
regopts = REG_EXTENDED;
opterr = 0;
Verbose = 0;
prg = argv[0];
IdPat = false;
AddrPat = false;
flags = EDB_OPEN_RDONLY|EDB_OPEN_NOENV;
while ((r = getopt(argc, argv, "I:R:rVvw")) != -1)
{
switch (r)
{
case 'I':
c = b_regcomp(&IdReg, optarg, regopts);
if (c != 0)
{
fprintf(stderr, "regcomp(%s) failed=%d\n",
optarg, c);
return 1;
}
IdPat = true;
break;
case 'R':
c = b_regcomp(&AddrReg, optarg, regopts);
if (c != 0)
{
fprintf(stderr, "regcomp(%s) failed=%d\n",
optarg, c);
return 1;
}
AddrPat = true;
break;
case 'r':
flags = EDB_OPEN_RDONLY;
break;
case 'v':
flags |= EDB_OPEN_NOVERSION;
break;
case 'V':
++Verbose;
break;
case 'w':
flags = EDB_OPEN_RDWR;
break;
default:
usage(prg);
/* NOTREACHED */
break;
}
}
sm_test_begin(argc, argv, "test edbr 1");
argc -= optind;
argv += optind;
edbr1(flags);
return sm_test_end();
}
syntax highlighted by Code2HTML, v. 0.9.1