/* * Copyright (c) 2004-2006 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: mailq.c,v 1.40 2007/06/18 04:42:31 ca Exp $") #include "sm/assert.h" #include "sm/magic.h" #include "sm/error.h" #include "sm/sysexits.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/str.h" #include "sm/cstr.h" #include "sm/bhtable.h" #include "sm/edb.h" #include "sm/actdb-int.h" #include "sm/regex.h" #include "sm/rdibdb.h" #include "sm/qmibdb.h" #define QMGR_DEBUG_DEFINE 1 #include "sm/qmgr-int.h" #include "sm/qmgrdbg.h" /* ** Print content of DEFEDB. How to do this? 1. read sequentially through defedb and add data to some structure? the data structure must allow access by ta_id. how to deal with reading a RCPT before the corresponding TA? add a dummy TA? note: aq_ta does not have a link to its RCPTs 2. read through defedb and collect TA records, then read through defedb and look for RCPT records, print the data when an entire TA has been read? What do we need? a list of all MAIL records and from there a list of the corresponding RCPTs however, aq_ta doesn't have a pointer to aq_rcpt (see above), but aq_rcpt has a pointer to aq_ta. Print content of IBDB. Problem: doesn't have all the data, e.g., size and time are missing */ /* HACK ... */ #define ERRBUF_SIZE 1024 #define MAX_MATCH 256 #define SMF_NODEFEDB 0x01 #define SMF_NOIBDB 0x02 static int Verbose; static regex_t IdReg; static regex_t AddrReg; static bool IdPat; static bool AddrPat; /* HACK */ #define MAX_STR_SZ 1024 #define BHTSIZE (32 * 1024) /* XXX Too much of this is a copy of some other functions, e.g., libqmgr/rdi* with some changes... XXX who creates/own strings (_pa, cdb)? */ /* context for *_action() calls (and probably others later on) */ typedef struct ib_ctx_S ib_ctx_T, *ib_ctx_P; struct ib_ctx_S { ibdbr_ctx_P ibx_ibdbrc; /* IBDB recovery context */ aq_ctx_P ibx_aq_ctx; /* AQ ctx */ aq_ta_P ibx_aq_ta; /* AQ TA */ aq_rcpt_P ibx_aq_rcpt; /* AQ RCPT */ bht_P ibx_bht_ta; /* bhtable TA */ bht_P ibx_bht_rcpt; /* bhtable RCPT */ time_T ibx_time; /* timestamp */ }; /* ** QM_RI_CTX_FREE -- free IBDB context ** ** Parameters: ** ib_ctx -- IBDB context ** ** Returns: ** SM_SUCCESS */ static sm_ret_T ib_ctx_free(ib_ctx_P ib_ctx) { if (NULL == ib_ctx) return SM_SUCCESS; if (ib_ctx->ibx_bht_ta != NULL) bht_destroy(ib_ctx->ibx_bht_ta, ibdb_ta_free, NULL); if (ib_ctx->ibx_bht_rcpt != NULL) bht_destroy(ib_ctx->ibx_bht_rcpt, ibdb_rcpt_free, NULL); aq_ta_free(ib_ctx->ibx_aq_ta); aq_rcpt_free(ib_ctx->ibx_aq_rcpt); sm_free_size(ib_ctx, sizeof(*ib_ctx)); return SM_SUCCESS; } /* ** QM_RI_CTX_NEW -- create new Recover-IBDB context ** ** Parameters: ** pri_ctx -- (pointer to) Recover-IBDB context (output) ** ** Returns: ** usual sm_error code. */ static sm_ret_T ib_ctx_new(ib_ctx_P *pri_ctx) { sm_ret_T ret; bht_P bht, bhr; ib_ctx_P ib_ctx; SM_REQUIRE(pri_ctx != NULL); bht = NULL; bhr = NULL; ret = sm_error_temp(SM_EM_Q_RDIBDB, ENOMEM); ib_ctx = (ib_ctx_P) sm_zalloc(sizeof(*ib_ctx)); if (NULL == ib_ctx) goto error; ret = sm_error_temp(SM_EM_Q_RDIBDB, ENOMEM); bht = bht_new(BHTSIZE, BHTSIZE); if (NULL == bht) goto error; ib_ctx->ibx_bht_ta = bht; bhr = bht_new(BHTSIZE, BHTSIZE); if (NULL == bhr) goto error; ib_ctx->ibx_bht_rcpt = bhr; ret = aq_ta_new(&ib_ctx->ibx_aq_ta); if (sm_is_err(ret)) goto error; ret = aq_rcpt_new(&ib_ctx->ibx_aq_rcpt); if (sm_is_err(ret)) goto error; *pri_ctx = ib_ctx; return SM_SUCCESS; error: if (bht != NULL) bht_destroy(bht, NULL, NULL); if (bhr != NULL) bht_destroy(bhr, NULL, NULL); if (ib_ctx != NULL) { aq_ta_free(ib_ctx->ibx_aq_ta); aq_rcpt_free(ib_ctx->ibx_aq_rcpt); sm_free_size(ib_ctx, sizeof(*ib_ctx)); } return ret; } /* ** B_REGMATCH -- check whether regex matches ** ** Parameters: ** preg -- regex ** string -- string to match ** eflags -- flags for regexec() ** ** Returns: ** regexec() */ 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; } /* ** QM_IBDB_TA_ACTION -- Add transaction to DEFEDB (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- context ** ** Returns: ** usual sm_error code */ static sm_ret_T ibdb_ta_action(bht_entry_P info, void *ctx) { sm_ret_T ret; ibdb_ta_P ibdb_ta; ib_ctx_P ib_ctx; aq_ta_P aq_ta; /* ** Do NOT return an error code because that will stop ** the recovery (bht_walk()). */ ib_ctx = (ib_ctx_P) ctx; ibdb_ta = (ibdb_ta_P) info->bhe_value; if (NULL == ibdb_ta->ibt_ta_id) return SM_SUCCESS; /* sm_error_perm(SM_EM_Q_RDIBDB, SM_E_UNEXPECTED); */ if ((IdPat && b_regmatch(&IdReg, ibdb_ta->ibt_ta_id, 0) != 0) || (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata(ibdb_ta->ibt_mail_pa), 0) != 0)) { return SM_SUCCESS; } /* already there? */ ret = aq_ta_find(ib_ctx->ibx_aq_ctx, ibdb_ta->ibt_ta_id, false, &aq_ta); if (sm_is_success(ret)) return SM_SUCCESS; ret = aq_ta_add_new(ib_ctx->ibx_aq_ctx, &aq_ta, AQ_TA_FL_DEFEDB, 1, THR_NO_LOCK); if (sm_is_err(ret)) return SM_SUCCESS; /* ret; */ /* fake time... aq_ta->aqt_st_time = ib_ctx->ibx_time; */ aq_ta->aqt_st_time = 0; aq_ta->aqt_rcpts_inaq = ibdb_ta->ibt_nrcpts; aq_ta->aqt_rcpts_tot = ibdb_ta->ibt_nrcpts; /* Who ensures that this is not too big? */ aq_ta->aqt_nxt_idx = ibdb_ta->ibt_nrcpts; aq_ta->aqt_rcpts_tot = ibdb_ta->ibt_nrcpts; aq_ta->aqt_rcpts_left = ibdb_ta->ibt_rcpts_left; aq_ta->aqt_rcpts_temp = ibdb_ta->ibt_rcpts_temp; aq_ta->aqt_rcpts_perm = ibdb_ta->ibt_rcpts_perm; aq_ta->aqt_rcpts_tried = ibdb_ta->ibt_nrcpts - ibdb_ta->ibt_rcpts_left; SESSTA_COPY(aq_ta->aqt_ss_ta_id, ibdb_ta->ibt_ta_id); aq_ta->aqt_cdb_id = ibdb_ta->ibt_cdb_id; ibdb_ta->ibt_cdb_id = NULL; aq_ta->aqt_mail->aqm_pa = ibdb_ta->ibt_mail_pa; ibdb_ta->ibt_mail_pa = NULL; return SM_SUCCESS; } /* ** QM_IBDB_RCPT_ACTION -- Add recipient to DEFEDB (callback function) ** ** Parameters: ** info -- bht entry ** ctx -- context ** ** Returns: ** usual sm_error code */ static sm_ret_T ibdb_rcpt_action(bht_entry_P info, void *ctx) { sm_ret_T ret; ibdb_rcpt_P ibdb_rcpt; ib_ctx_P ib_ctx; aq_rcpt_P aq_rcpt, aq_rcpt_f; /* ** Do NOT return an error code because that will stop ** the recovery (bht_walk()). */ ib_ctx = (ib_ctx_P) ctx; ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value; if (NULL == ibdb_rcpt->ibr_ta_id) return SM_SUCCESS; /* sm_error_perm(SM_EM_Q_RDIBDB, SM_E_UNEXPECTED); */ if ((IdPat && b_regmatch(&IdReg, ibdb_rcpt->ibr_ta_id, 0) != 0) || (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata(ibdb_rcpt->ibr_pa), 0) != 0)) { return SM_SUCCESS; } /* already in AQ? */ ret = aq_rcpt_find_ss(ib_ctx->ibx_aq_ctx, ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx, THR_NO_LOCK, &aq_rcpt); if (sm_is_success(ret)) return SM_SUCCESS; ret = aq_rcpt_add_new(ib_ctx->ibx_aq_ctx, NULL, &aq_rcpt, AQ_TA_FL_DEFEDB, THR_NO_LOCK); if (sm_is_err(ret)) return SM_SUCCESS; /* ret; */ /* Let's copy it over... */ SESSTA_COPY(aq_rcpt->aqr_ss_ta_id, ibdb_rcpt->ibr_ta_id); aq_rcpt->aqr_pa = ibdb_rcpt->ibr_pa; ibdb_rcpt->ibr_pa = NULL; aq_rcpt->aqr_da_idx = 0; aq_rcpt->aqr_st_time = ib_ctx->ibx_time; aq_rcpt->aqr_entered = ib_ctx->ibx_time; aq_rcpt->aqr_idx = ibdb_rcpt->ibr_idx; aq_rcpt->aqr_status = AQR_ST_NEW; AQR_DA_INIT(aq_rcpt); AQR_SS_INIT(aq_rcpt); ret = aq_rcpt_find_one_ss(ib_ctx->ibx_aq_ctx, aq_rcpt->aqr_ss_ta_id, THR_NO_LOCK, &aq_rcpt_f); if (SM_SUCCESS == ret && aq_rcpt_f != NULL && aq_rcpt_f != aq_rcpt) { AQR_SS_APP(aq_rcpt_f, aq_rcpt); aq_rcpt_f = NULL; } return SM_SUCCESS; } /* ** RIBDB -- read INCEDB records and discard "closed" transactions. ** ** Parameters: ** ib_ctx -- Recover-IBDB context ** ** Returns: ** usual sm_error code. ** ** Input: ib_ctx contains initialized values for: ** ibx_ibdbrc ** ibx_bht_ta ** ibx_bht_rcpt ** ** Output: ib_ctx contains (possibly) updated values for: ** ibx_bht_ta ** ibx_bht_rcpt ** ** Note: currently this function calls bht_walk for TA and RCPT, ** hence the other values in ib_ctx must be initialized too. */ static sm_ret_T ribdb(ib_ctx_P ib_ctx) { int status; sm_ret_T ret; bool more; ibdbr_ctx_P ibdbrc; ibdb_ta_P ibdb_ta, ibdb_lta; ibdb_rcpt_P ibdb_rcpt, ibdb_lrcpt; ibdb_hdrmod_P ibdb_hdrmod; bht_P bht_ta, bht_rcpt; bht_entry_P bhte; rcpt_id_P rcpt_id; ibdbrc = ib_ctx->ibx_ibdbrc; bht_ta = ib_ctx->ibx_bht_ta; bht_rcpt = ib_ctx->ibx_bht_rcpt; ret = qm_ibdb_ta_new(&ibdb_ta); if (sm_is_err(ret) || NULL == ibdb_ta) goto end; ret = qm_ibdb_rcpt_new(&ibdb_rcpt); if (sm_is_err(ret) || NULL == ibdb_rcpt) goto end; ret = ibdbr_hdrmod_new(&ibdb_hdrmod); if (sm_is_err(ret) || NULL == ibdb_hdrmod) goto end; more = true; do { ret = ibdbr_get(ibdbrc, ibdb_rcpt, ibdb_ta, ibdb_hdrmod , &status); switch (ret) { case RT_IBDB_TA: if (IBDB_TA_NEW == status) { ret = bht_add(bht_ta, ibdb_ta->ibt_ta_id, SMTP_STID_SIZE, ibdb_ta, &bhte); if (sm_is_err(ret)) { /* XXX what to do now? */ QM_LEV_DPRINTF(0, (QM_DEBFP, "ERROR: bht_add=%x, ta=%s\n", ret, ibdb_ta->ibt_ta_id)); goto end; } ret = qm_ibdb_ta_new(&ibdb_ta); if (sm_is_err(ret) || NULL == ibdb_ta) goto end; } else if (IBDB_TA_CANCEL == status) { qm_ibdb_cancel_ta(bht_ta, bht_rcpt, ibdb_ta->ibt_ta_id); qm_ibdb_ta_clean(ibdb_ta); } else { ibdb_lta = bht_find(bht_ta, ibdb_ta->ibt_ta_id, SMTP_STID_SIZE); qm_ibdb_ta_clean(ibdb_ta); /* might happen if some logfiles are removed */ if (NULL == ibdb_lta) break; bht_rm(bht_ta, ibdb_ta->ibt_ta_id, SMTP_STID_SIZE, ibdb_ta_free, NULL); } break; case RT_IBDB_RCPT: if (IBDB_RCPT_NEW == status) { rcpt_id = (char *) sm_malloc(SMTP_RCPTID_SIZE + 1); if (NULL == rcpt_id) { ret = sm_error_temp(SM_EM_Q_RDIBDB, ENOMEM); goto end; } sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx); ret = bht_add(bht_rcpt, rcpt_id, SMTP_RCPTID_SIZE, ibdb_rcpt, &bhte); if (sm_is_err(ret)) { /* XXX what to do now? */ QM_LEV_DPRINTF(0, (QM_DEBFP, "ERROR: bht_add=%x, rcpt_id=%s\n", ret, rcpt_id)); goto end; } ret = qm_ibdb_rcpt_new(&ibdb_rcpt); if (sm_is_err(ret) || NULL == ibdb_rcpt) goto end; } else { rcpt_id_T lrcpt_id; sm_snprintf(lrcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx); ibdb_lrcpt = bht_find(bht_rcpt, lrcpt_id, SMTP_RCPTID_SIZE); /* might happen if some logfiles are removed */ if (NULL == ibdb_lrcpt) break; ibdb_lta = bht_find(bht_ta, ibdb_rcpt->ibr_ta_id, SMTP_STID_SIZE); if (ibdb_lta != NULL) { if (IBDB_RCPT_TEMP == status) ibdb_lta->ibt_rcpts_temp++; else if (IBDB_RCPT_PERM == status) ibdb_lta->ibt_rcpts_perm++; } bht_rm(bht_rcpt, lrcpt_id, SMTP_RCPTID_SIZE, ibdb_rcpt_free, NULL); } break; case RT_IBDB_HDRMOD: break; default: if (sm_is_err(ret) && sm_error_value(ret) == ENOENT) ret = SM_SUCCESS; else QM_LEV_DPRINTF(0, (QM_DEBFP, "ERROR: ibdbr_get=%x\n", ret)); more = false; break; } } while (more); bht_walk(bht_ta, ibdb_ta_action, ib_ctx); bht_walk(bht_rcpt, ibdb_rcpt_action, ib_ctx); end: qm_ibdb_ta_free(ibdb_ta); ibdb_ta = NULL; qm_ibdb_rcpt_free(ibdb_rcpt); ibdb_rcpt = NULL; ibdbr_hdrmod_free(ibdb_hdrmod); return ret; } static sm_ret_T printtime(time_T t, char *name) { if (Verbose > 1) sm_io_fprintf(smioout, "%s=%ld %s", name, (long) t, ctime(&t)); else sm_io_fprintf(smioout, "%s=%ld\n", name, (long) t); return SM_SUCCESS; } static sm_ret_T prt_mail(aq_ta_P aq_ta) { off_t sz; sz = aq_ta->aqt_msg_sz_b; sm_io_fprintf(smioout, "%s ", aq_ta->aqt_ss_ta_id); if (sz == 0) sm_io_fprintf(smioout, "unknown "); else sm_io_fprintf(smioout, "%7lu ", (ulong) sz / ONEKB); if (aq_ta->aqt_st_time == 0) sm_io_fprintf(smioout, "%.16s", "(incoming) "); else sm_io_fprintf(smioout, "%.16s", ctime(&aq_ta->aqt_st_time)); sm_io_fprintf(smioout, " %S\n", aq_ta->aqt_mail->aqm_pa); return SM_SUCCESS; } static sm_ret_T print_mail(aq_ta_P aq_ta) { uint u; if (Verbose <= 1) return prt_mail(aq_ta); sm_io_fprintf(smioout, "\nta_id=%s\n", aq_ta->aqt_ss_ta_id); sm_io_fprintf(smioout, "mail=%S\n", aq_ta->aqt_mail->aqm_pa); printtime(aq_ta->aqt_st_time, "time"); sm_io_fprintf(smioout, "cdb=%s\n", sm_cstr_data(aq_ta->aqt_cdb_id)); sm_io_fprintf(smioout, "rcpts_tot=%u\n", aq_ta->aqt_rcpts_tot); sm_io_fprintf(smioout, "rcpts_left=%u\n", aq_ta->aqt_rcpts_left); sm_io_fprintf(smioout, "rcpts_temp=%u\n", aq_ta->aqt_rcpts_temp); sm_io_fprintf(smioout, "rcpts_perm=%u\n", aq_ta->aqt_rcpts_perm); if (Verbose > 0) { sm_io_fprintf(smioout, "rcpts_tried=%u\n", aq_ta->aqt_rcpts_tried); sm_io_fprintf(smioout, "nxt_idx=%u\n", aq_ta->aqt_nxt_idx); } sm_io_fprintf(smioout, "state=%d\n", aq_ta->aqt_state); sm_io_fprintf(smioout, "aqt_rcpts_ar=%d\n", aq_ta->aqt_rcpts_ar); sm_io_fprintf(smioout, "aqt_owners_n=%d\n", aq_ta->aqt_owners_n); for (u = 0; u < aq_ta->aqt_owners_n; u++) { sm_io_fprintf(smioout, "owner[%u]=%S\n", u, aq_ta->aqt_owners_pa[u]); } return SM_SUCCESS; } static sm_ret_T prt_rcpt(aq_rcpt_P aq_rcpt) { sm_io_fprintf(smioout, "%*s%S\n" , 45, "" , aq_rcpt->aqr_pa); if (Verbose > 0) { sm_ret_T ret; sm_str_P errmsg; errmsg = sm_str_new(NULL, 80, 256); ret = SM_SUCCESS; if (aq_rcpt->aqr_msg != NULL && sm_str_getlen(aq_rcpt->aqr_msg) > 0) sm_io_fprintf(smioout, "%*s%.35S\n" , 45, "" , aq_rcpt->aqr_msg); else if (errmsg != NULL && sm_is_success(ret = aq_rcpt_status2txt(aq_rcpt, errmsg)) && sm_str_getlen(errmsg) > 0) { sm_io_fprintf(smioout, "%*s%.35S\n" , 45, "" , errmsg); } if (errmsg != NULL) sm_str_clr(errmsg); if (errmsg != NULL && ret != AQR_TXT_COMPLETE && sm_is_success(aq_rcpt_err_state(aq_rcpt, false, errmsg)) && sm_str_getlen(errmsg) > 0) { sm_io_fprintf(smioout, "%*s%.35S\n" , 45, "" , errmsg); } SM_STR_FREE(errmsg); } return SM_SUCCESS; } static sm_ret_T print_rcpt(aq_rcpt_P aq_rcpt) { uint u; if (Verbose <= 1) return prt_rcpt(aq_rcpt); sm_io_fprintf(smioout, "\nrecipient\n"); sm_io_fprintf(smioout, "ta_id=%s\n", aq_rcpt->aqr_ss_ta_id); sm_io_fprintf(smioout, "rcpt=%S\n", aq_rcpt->aqr_pa); sm_io_fprintf(smioout, "aqr_rcpt_idx=%u\n", aq_rcpt->aqr_idx); if (Verbose > 0) sm_io_fprintf(smioout, "aqr_tries=%u\n", aq_rcpt->aqr_tries); if (Verbose > 0 && aq_rcpt->aqr_port != 0) sm_io_fprintf(smioout, "aqr_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, "srv_ip4=%A\n", aq_rcpt->aqr_addrs[u].aqra_ipv4); printtime(aq_rcpt->aqr_addrs[u].aqra_expt, "expt"); sm_io_fprintf(smioout, "pref=%hu\n", aq_rcpt->aqr_addrs[u].aqra_pref); } } else { sm_io_fprintf(smioout, "srv_ip4=%A\n", aq_rcpt->aqr_addrs[0].aqra_ipv4); } } else sm_io_fprintf(smioout, "srv_ip4=undef\n"); sm_io_fprintf(smioout, "aqr_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, "aqr_status=%d\n", aq_rcpt->aqr_status); if (Verbose > 0) sm_io_fprintf(smioout, "flags=0x%x\n", aq_rcpt->aqr_flags); if (Verbose > 0 && aq_rcpt->aqr_msg != NULL) sm_io_fprintf(smioout, "text=%S\n", aq_rcpt->aqr_msg); if (Verbose > 0 && aq_rcpt->aqr_dsn_msg != NULL) sm_io_fprintf(smioout, "dsn_msg=%S\n", aq_rcpt->aqr_dsn_msg); if (Verbose > 0 && aq_rcpt->aqr_err_st != 0) sm_io_fprintf(smioout, "err_st=0x%x\n", aq_rcpt->aqr_err_st); if (Verbose > 0 && aq_rcpt->aqr_addr_fail != 0) sm_io_fprintf(smioout, "host=%A\n", aq_rcpt->aqr_addr_fail); if (Verbose > 0 && aq_rcpt_has_bounce(aq_rcpt)) sm_io_fprintf(smioout, "bounce_idx=%u\n", aq_rcpt->aqr_dsn_idx); if (Verbose > 0 && aq_rcpt_has_owner(aq_rcpt)) sm_io_fprintf(smioout, "owner_idx=%u\n", aq_rcpt->aqr_owner_idx); return SM_SUCCESS; } static sm_ret_T print_header(void) { sm_io_fprintf(smioout, "-----SS_TA_ID------ -Size-- -----Q-Time----- --------Sender/Recipient-----------\n"); return SM_SUCCESS; } static sm_ret_T printq(aq_ctx_P aq_ctx) { sm_ret_T ret; uint entries; aq_rcpt_P aq_rcpt, aq_rcpt_nxt; aq_ta_P aq_ta, aq_ta_nxt; SM_IS_AQ(aq_ctx); entries = 0; for (aq_ta = AQ_TAS_FIRST(aq_ctx); aq_ta != AQ_TAS_END(aq_ctx); aq_ta = aq_ta_nxt) { if (entries == 0) print_header(); ++entries; aq_ta_nxt = AQ_TAS_NEXT(aq_ta); print_mail(aq_ta); ret = aq_rcpt_find_one_ss(aq_ctx, aq_ta->aqt_ss_ta_id, THR_NO_LOCK, &aq_rcpt); if (SM_SUCCESS == ret) { aq_rcpt_nxt = aq_rcpt; do { print_rcpt(aq_rcpt_nxt); aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt); } while (aq_rcpt_nxt != aq_rcpt); } } if (entries == 0) sm_io_fprintf(smioout, "queue is empty\n"); return SM_SUCCESS; } /* ** RDIBDB -- read INCEDB ** ** Parameters: ** aq_ctx -- AQ context ** ibdb_basedir -- base dir for IBDB ** ** Returns: ** usual sm_error code. */ static sm_ret_T rdibdb(aq_ctx_P aq_ctx, char *ibdb_basedir) { sm_ret_T ret; uint32_t first, last; ib_ctx_P ib_ctx; ibdbr_ctx_P ibdbrc; ibdbrc = NULL; ret = ib_ctx_new(&ib_ctx); if (sm_is_err(ret)) { QM_LEV_DPRINTF(0, (QM_DEBFP, "sev=ERROR, func=qm_rdibdb, qm_ri_ctx_new=%x\n", ret)); goto end; } /* open IBDB directory */ ret = ibdbf_get_seq(ibdb_basedir, IBDB_NAME, IBDB_OFL_WRITE, &first, &last); QM_LEV_DPRINTF(4, (QM_DEBFP, "sev=DBG, func=qm_rdibdb, ibdbf_get_seq=%x, first=%u, last=%u\n" , ret, first, last)); /* fixme: distinguish error values? e.g., no directory? */ if (sm_is_err(ret)) { ret = SM_SUCCESS; goto end; /* XXX no data? */ } ret = ibdbr_open(ibdb_basedir, IBDB_NAME, SM_IO_RDONLY, first, 0 /* unused */, IBDB_OFL_WRITE, &ibdbrc); if (sm_is_err(ret)) goto end; ib_ctx->ibx_ibdbrc = ibdbrc; /* evthr_time() isn't working yet; the system isn't running. */ ib_ctx->ibx_time = time(NULLT); ib_ctx->ibx_aq_ctx = aq_ctx; ret = ribdb(ib_ctx); #if 0 if (sm_is_success(ret)) printq(aq_ctx); #endif /* 0 */ end: if (ibdbrc != NULL) { (void) ibdbr_close(ibdbrc); /* XXX Complain on error? */ ibdbrc = NULL; } ib_ctx_free(ib_ctx); return ret; } /* ** RDDEFEDB -- read defedb queue ** ** Parameters: ** edb_basedir -- base dir for DEFEDB ** oflags -- open flags for DEFEDB ** ** Returns: ** usual sm_error code. */ static sm_ret_T rddefedb(aq_ctx_P aq_ctx, char *edb_basedir, uint oflags) { sm_ret_T ret, rt; edb_cnf_T edb_cnf; aq_ta_P aq_ta; aq_rcpt_P aq_rcpt, aq_rcpt_f; edb_ctx_P edb_ctx; edb_req_P edb_req; edb_cursor_P edb_cursor; char curdir[PATH_MAX]; aq_rcpt = NULL; aq_ta = NULL; edb_req = NULL; edb_cursor = NULL; edb_ctx = NULL; curdir[0] = '\0'; sm_memzero(&edb_cnf, sizeof(edb_cnf)); edb_cnf.edbcnf_oflags = oflags; if (edb_basedir != NULL && *edb_basedir != '\0') { int r; getcwd(curdir, sizeof(curdir)); r = chdir(edb_basedir); if (r != 0) { ret = sm_error_perm(SM_EM_UTIL, errno); return ret; } } ret = edb_open(&edb_cnf, (const char*)0, NULL, &edb_ctx); if (sm_is_err(ret)) return ret; ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, false); if (sm_is_err(ret)) goto error; ret = edb_rd_open(edb_ctx, &edb_cursor); 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; if (sm_is_err(ret)) goto error; rt = edb_get_type(edb_req); if (sm_is_err(ret)) goto error; if (EDB_REQ_TA == rt) { ret = aq_ta_add_new(aq_ctx, &aq_ta, AQ_TA_FL_DEFEDB, 1, THR_NO_LOCK); if (sm_is_err(ret)) goto error; ret = edb_ta_dec(edb_req, aq_ta); 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) || (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata( aq_ta->aqt_mail->aqm_pa), 0) != 0)) { ret = aq_ta_rm(aq_ctx, aq_ta, false); continue; } if (Verbose > 4) print_mail(aq_ta); /* for checks... */ aq_ta->aqt_rcpts_inaq = aq_ta->aqt_rcpts_left; aq_ta = NULL; } else if (EDB_REQ_RCPT == rt) { ret = aq_rcpt_add_new(aq_ctx, NULL, &aq_rcpt, AQ_TA_FL_DEFEDB, THR_NO_LOCK); if (sm_is_err(ret)) goto error; AQR_DA_INIT(aq_rcpt); AQR_SS_INIT(aq_rcpt); ret = edb_rcpt_dec(edb_req, aq_rcpt); 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) || (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata(aq_rcpt->aqr_pa), 0) != 0)) { ret = aq_rcpt_rm(aq_ctx, aq_rcpt, 0); continue; } if (Verbose > 4) print_rcpt(aq_rcpt); ret = aq_rcpt_find_one_ss(aq_ctx, aq_rcpt->aqr_ss_ta_id, THR_NO_LOCK, &aq_rcpt_f); if (SM_SUCCESS == ret && aq_rcpt_f != NULL && aq_rcpt_f != aq_rcpt) { AQR_SS_APP(aq_rcpt_f, aq_rcpt); aq_rcpt_f = NULL; } aq_rcpt = NULL; } else if (rt != EDB_REQ_VRS) { sm_io_fprintf(smioout, "found unknown data in queue, recordtype=%x\n" , rt); } } if (curdir[0] != '\0') { /* change back to original directory */ (void) chdir(curdir); /* ignore error for now */ } error: SM_FREE(aq_rcpt); sm_io_flush(smioout); if (edb_ctx != NULL && edb_cursor != NULL) ret = edb_rd_close(edb_ctx, edb_cursor); if (edb_ctx != NULL) ret = edb_close(edb_ctx); return ret; } /* ** RDPRTQ -- read and display content of mail queues ** ** Parameters: ** edb_basedir -- base dir for DEFEDB ** ibdb_basedir -- base dir for IBDB ** oflags -- open flags for DEFEDB ** flags -- various flags ** aq_size -- max size of AQ ** ** Returns: ** usual sm_error code. */ static sm_ret_T rdprtq(char *edb_basedir, char *ibdb_basedir, uint oflags, uint flags, uint aq_size) { sm_ret_T ret; aq_ctx_P aq_ctx; ret = aq_open(NULL, &aq_ctx, aq_size, 0); if (sm_is_err(ret)) goto error; if (!SM_IS_FLAG(flags, SMF_NODEFEDB)) rddefedb(aq_ctx, edb_basedir, oflags); if (!SM_IS_FLAG(flags, SMF_NOIBDB)) rdibdb(aq_ctx, ibdb_basedir); printq(aq_ctx); error: sm_io_flush(smioout); (void) aq_close(aq_ctx); return ret; } 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); sm_io_fprintf(smioerr, "pattern-compile-error: %s", errbuf); } return regerr; } static void usage(const char *prg) { sm_io_fprintf(smioerr, "usage: %s [options]\n" "Print content of mail queues.\n" "Options:\n" "-B dir Specify base directory for defedb and ibdb [default: .]\n" "-D dir Specify defedb base directory [default: .]\n" "-d Do not read defedb\n" "-I dir Specify ibdb base directory [default: .]\n" "-i Do not read ibdb\n" "-M 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" , prg); exit(EX_USAGE); } int main(int argc, char *argv[]) { int r, c, regopts; uint oflags, flags; sm_ret_T ret; char *prg, *edb_basedir, *ibdb_basedir; prg = argv[0]; if (getuid() == 0 || geteuid() == 0) { sm_io_fprintf(smioerr, "%s: ERROR: do not run this as super-user!\n", prg); exit(EX_USAGE); } ibdb_basedir = edb_basedir = NULL; regopts = REG_EXTENDED; opterr = 0; Verbose = 0; IdPat = false; AddrPat = false; oflags = EDB_OPEN_RDONLY|EDB_OPEN_NOENV; flags = 0; while ((r = getopt(argc, argv, "B:D:dI:iM:R:V")) != -1) { switch (r) { case 'B': edb_basedir = strdup(optarg); if (NULL == edb_basedir) { sm_io_fprintf(smioerr, "strdup(%s) failed\n", optarg); return EX_OSERR; } ibdb_basedir = edb_basedir; break; case 'D': edb_basedir = strdup(optarg); if (NULL == edb_basedir) { sm_io_fprintf(smioerr, "strdup(%s) failed\n", optarg); return EX_OSERR; } break; case 'd': flags |= SMF_NODEFEDB; break; case 'I': ibdb_basedir = strdup(optarg); if (NULL == ibdb_basedir) { sm_io_fprintf(smioerr, "strdup(%s) failed\n", optarg); return EX_OSERR; } break; case 'i': flags |= SMF_NOIBDB; break; case 'M': c = b_regcomp(&IdReg, optarg, regopts); if (c != 0) { sm_io_fprintf(smioerr, "regcomp(%s) failed=%d\n", optarg, c); return 1; } IdPat = true; break; case 'R': c = b_regcomp(&AddrReg, optarg, regopts); if (c != 0) { sm_io_fprintf(smioerr, "regcomp(%s) failed=%d\n", optarg, c); return 1; } AddrPat = true; break; case 'V': ++Verbose; break; default: usage(prg); /* NOTREACHED */ break; } } argc -= optind; argv += optind; ret = rdprtq(edb_basedir, ibdb_basedir, oflags, flags, 1024 * 1024); if (sm_is_err(ret)) sm_io_fprintf(smioerr, "error: %s\n", smerr2txt(ret)); return ret; }