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