/* * 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: ibdbc.c,v 1.17 2006/08/05 04:19:29 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/time.h" #include "sm/heap.h" #include "sm/rpool.h" #include "sm/bhtable.h" #include "sm/ibdb.h" #include "ibdbc.h" /* ** IBDB Cache: ** ** This cache (organized as hash table: ibdb_ctx->ibdb_ct, index is ** TA/RCPT id) keeps track in which IBDB sequence number a record (TA ** or RCPT) is stored. ** ** When an entry is added to IBDB, it is also added to the cache: ** sm_ibc_ta_add() and sm_ibc_rcpt_add(). ** ** When an entry is closed in IBDB, then it is removed from the cache: ** sm_ibc_ta_rm() and sm_ibc_rcpt_rm(). ** ** One walk through the cache can be used to determine the lowest ** sequence number that is still referenced sm_ibc_low_seq(). Any IBDB ** file that has a lower sequence number can be removed, see ** libibdb/ibdb.c: ibdb_clean(). */ /* ** Problems: ** what to do when the cache is full? ** a graceful fallback to another cleanup algorithm would be the best, ** but that's overkill for the first version. ** ** Todo: ** Is it possible to count references to an IBDB files? ** that is, create an array of used files (first - seq) ** and then increase a (reference) counter for each open ** transaction in a file? ** Probably not: a file without "references" may contain a "CLOSE" ** transaction for an "OPEN" transaction in an earlier file. ** Removing the latter will breaks things... */ /* ** IBC_E_NEW -- allocate IBDB cache entry ** ** Parameters: ** pibc_e -- (pointer to) IBDB cache entry (output) ** ** Returns: ** usual sm_error code; ENOMEM ** ** Side Effects: none on error ** ** Last code review: 2005-03-30 23:18:58 ** Last code change: */ static sm_ret_T ibc_e_new(ibc_e_P *pibc_e) { ibc_e_P ibc_e; SM_REQUIRE(pibc_e != NULL); ibc_e = (ibc_e_P) sm_zalloc(sizeof(*ibc_e)); if (NULL == ibc_e) return sm_error_temp(SM_EM_IBDB, ENOMEM); *pibc_e = ibc_e; return SM_SUCCESS; } /* ** IBC_E_FREE -- free IBDB cache entry ** ** Parameters: ** ibc_e -- IBDB cache entry ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-03-30 23:46:02 ** Last code change: */ static sm_ret_T ibc_e_free(ibc_e_P ibc_e) { if (NULL == ibc_e) return SM_SUCCESS; sm_free_size(ibc_e, sizeof(*ibc_e)); return SM_SUCCESS; } /* ** BHT_IBC_E_FREE -- free IBDB cache entry (callback function) ** ** Parameters: ** value -- IBDB cache entry ** key -- rcpt id (unused) ** ctx -- context (ignored, for compatibility with bhfree_F) ** ** Returns: ** none ** ** Last code review: 2005-03-30 23:45:46 ** Last code change: */ /* ARGSUSED1 */ static void bht_ibc_e_free(void *value, void *key, void *ctx) { ibc_e_P ibc_e; if (NULL == value) return; ibc_e = (ibc_e_P) value; sm_free_size(ibc_e, sizeof(*ibc_e)); } /* ** SM_IBC_TA_ADD -- Add a new TA to IBC ** ** Parameters: ** bht -- Hash table ** ta_id -- Transaction ID ** seq -- sequence number ** ** Returns: ** usual sm_error code; ENOMEM, SM_E_FULL ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) ** ** Last code review: 2005-03-31 04:53:56 ** Last code change: 2005-03-31 04:53:31 */ sm_ret_T sm_ibc_ta_add(bht_P bht, sessta_id_P ta_id, uint32_t seq) { sm_ret_T ret; ibc_e_P ibc_e; bht_entry_P bhte; ret = ibc_e_new(&ibc_e); if (sm_is_err(ret)) return ret; SESSTA_COPY(ibc_e->ibc_id, ta_id); ibc_e->ibc_sequence = seq; ibc_e->ibc_type = IBC_TA; ret = bht_add(bht, ibc_e->ibc_id, SMTP_STID_SIZE, ibc_e, &bhte); return ret; } /* ** SM_IBC_RCPT_ADD -- Add a new RCPT to IBC ** ** Parameters: ** bht -- Hash table ** ta_id -- Transaction ID ** rcpt_idx -- Transaction ID ** seq -- sequence number ** ** Returns: ** usual sm_error code; ENOMEM, SM_E_FULL ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) ** ** Last code review: 2005-03-30 23:33:31 ** Last code change: 2005-03-30 23:20:19 */ sm_ret_T sm_ibc_rcpt_add(bht_P bht, sessta_id_P ta_id, rcpt_idx_T rcpt_idx, uint32_t seq) { sm_ret_T ret; ibc_e_P ibc_e; bht_entry_P bhte; ret = ibc_e_new(&ibc_e); if (sm_is_err(ret)) return ret; sm_snprintf(ibc_e->ibc_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, ta_id, rcpt_idx); ibc_e->ibc_sequence = seq; ibc_e->ibc_type = IBC_RCPT; ret = bht_add(bht, ibc_e->ibc_id, SMTP_RCPTID_SIZE, ibc_e, &bhte); if (sm_is_err(ret)) ibc_e_free(ibc_e); return ret; } /* ** SM_IBC_TA_RM -- Remove a TA from IBC ** ** Parameters: ** bht -- Hash table ** ta_id -- Transaction ID ** ** Returns: ** usual sm_error code; SM_E_NOTFOUND ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) ** ** Last code review: 2005-03-31 04:52:23 ** Last code change: */ sm_ret_T sm_ibc_ta_rm(bht_P bht, sessta_id_P ta_id) { ibc_e_P ibc_e; ibc_e = bht_find(bht, ta_id, SMTP_STID_SIZE); if (NULL == ibc_e) return sm_error_perm(SM_EM_IBDB, SM_E_NOTFOUND); bht_rm(bht, ta_id, SMTP_STID_SIZE, bht_ibc_e_free, NULL); return SM_SUCCESS; } /* ** SM_IBC_RCPT_RM -- Remove a RCPT from IBC ** ** Parameters: ** bht -- Hash table ** ta_id -- Transaction ID ** rcpt_idx -- Transaction ID ** ** Returns: ** usual sm_error code; SM_E_NOTFOUND ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) ** ** Last code review: 2005-03-30 23:46:42 ** Last code change: 2005-03-30 23:43:55 */ sm_ret_T sm_ibc_rcpt_rm(bht_P bht, sessta_id_P ta_id, rcpt_idx_T rcpt_idx) { ibc_e_P ibc_e; rcpt_id_T rcpt_id; sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE, SMTP_RCPTID_FORMAT, ta_id, rcpt_idx); ibc_e = bht_find(bht, rcpt_id, SMTP_RCPTID_SIZE); if (NULL == ibc_e) return sm_error_perm(SM_EM_IBDB, SM_E_NOTFOUND); bht_rm(bht, rcpt_id, SMTP_RCPTID_SIZE, bht_ibc_e_free, NULL); return SM_SUCCESS; } /* ** SM_IBC_WALK -- Callback function: determine smallest sequence number in bht ** ** Parameters: ** bhte -- Hash table entry ** ctx -- pointer to current lowest value ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-03-31 04:41:28 ** Last code change: */ static sm_ret_T sm_ibc_walk(bht_entry_P bhte, void *ctx) { ibc_e_P ibc_e; uint32_t *pseq; SM_REQUIRE(bhte != NULL); SM_REQUIRE(ctx != NULL); ibc_e = (ibc_e_P) bhte->bhe_value; pseq = (uint32_t *) ctx; if (ibc_e->ibc_sequence < *pseq) *pseq = ibc_e->ibc_sequence; return SM_SUCCESS; } /* ** SM_IBC_LOW_SEQ -- determine smallest sequence number in bht ** ** Parameters: ** bht -- Hash table ** pseq -- pointer to sequence number (output) ** ** Returns: ** SM_SUCCESS ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) ** ** Last code review: 2005-03-31 04:41:44 ** Last code change: */ sm_ret_T sm_ibc_low_seq(bht_P bht, uint32_t *pseq) { sm_ret_T ret; SM_REQUIRE(bht != NULL); SM_REQUIRE(pseq != NULL); *pseq = UINT32_MAX; ret = bht_walk(bht, sm_ibc_walk, pseq); if (*pseq == UINT32_MAX) *pseq = 0; return ret; }