/*
* 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: occh.c,v 1.34 2007/01/20 15:48:33 ca Exp $")
#include "sm/types.h"
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"
#include "sm/heap.h"
#include "sm/memops.h"
#include "sm/time.h"
#include "sm/mta.h"
#include "sm/qmgrdbg.h"
#include "occ.h"
#include "sm/io.h"
#define LOCK_DPRINTF(x)
/*
** Notes:
** Locking is currently done via dadb_ctx (2004-02-10)
**
** It might be useful to add a "free" function that actually
** frees entries instead of returning them to the freelist.
** Maybe this should just be a parameter to occ_entry_free()?
** Theoretically it would also be possible to count the number
** of entries in the "free-list" and free entries when a certain
** threshold is exceeded. However, this isn't as useful as it may seem
** because the number of outgoing connections is restricted
** (by the number of threads in the DAs), hence the number of entries
** in the "free-list" doesn't grow without bounds.
*/
/*
** OCC_ENTRY_FREE -- free OCC entry (return it to free-list)
**
** Parameters:
** occ_fl_hd -- head of OCC freelist
** occ_entry -- OCC entry
** occ_mutex -- mutex to use for locking
** locktype -- kind of locking
**
** Returns:
** SM_SUCCESS except for (un)lock errors
**
** Side Effects: none on error (except if unlock fails)
**
** Locking: locks occ_mutex if requested
**
** Last code review: 2005-03-17 04:52:37
** Last code change:
*/
sm_ret_T
occ_entry_free(occ_entry_hd_P occ_fl_hd, occ_entry_P occ_entry, pthread_mutex_t *occ_mutex, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "occ_entry_free"
int r;
if (NULL == occ_entry)
return SM_SUCCESS;
if (thr_lock_it(locktype)) {
r = smthread_mutex_lock(occ_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_error_perm(SM_EM_OCC, r);
}
SM_IS_OCCE(occ_entry);
occ_entry->occe_srv_ipv4 = 0;
occ_entry->occe_flags = 0;
occ_entry->occe_init_conc = 0;
occ_entry->occe_cur_conc = 0;
occ_entry->occe_max_conc = 0;
occ_entry->occe_open_se = 0;
occ_entry->occe_open_ta = 0;
#if 0
occ_entry->occe_last_conn = 0;
#endif
occ_entry->occe_last_upd = 0;
occ_entry->occe_timeout = OCCE_TO;
OCCFL_PRE(occ_fl_hd, occ_entry);
/* do not reset occ_entry->sm_magic, the entry can be reused */
if (thr_unl_always(locktype)) {
r = smthread_mutex_unlock(occ_mutex);
SM_ASSERT(r == 0);
if (r != 0)
return sm_error_perm(SM_EM_OCC, r);
}
return SM_SUCCESS;
}
#if 0
///*
//** OCC_ENTRY_GET -- get/create new OCC entry
//**
//** Parameters:
//** dadb_ctx -- DADB context
//** pocc_entry -- pointer to OCC entry (output)
//** locktype -- kind of locking
//**
//** Returns:
//** usual sm_error code
//**
//** Locking: locks dadb_ctx if requested
//*/
//
//sm_ret_T
//occ_entry_get(dadb_ctx_P dadb_ctx, occ_entry_P *pocc_entry, thr_lock_T locktype)
//{
// sm_ret_T ret;
// int r;
// occ_entry_P occ_entry;
//
// SM_IS_DADB(dadb_ctx);
// if (thr_lock_it(locktype))
// {
// r = smthread_mutex_lock(occ_mutex);
// SM_LOCK_OK(r);
// if (r != 0)
// return sm_error_perm(SM_EM_OCC, r);
// }
//
// if (OCCFL_EMPTY(occ_fl_hd))
// {
// occ_entry = (occ_entry_P) sm_zalloc(sizeof(*occ_entry));
// if (NULL == occ_entry)
// goto enomem;
//#if OCC_CHECK
// occ_entry->sm_magic = SM_OCCE_MAGIC;
//#endif
// }
// else
// {
// occ_entry = OCCFL_FIRST(occ_fl_hd);
// SM_IS_OCCE(occ_entry);
// OCCFL_REMOVE(occ_fl_hd);
// }
//
// *pocc_entry = occ_entry;
// if (thr_unl_no_err(locktype))
// {
// r = smthread_mutex_unlock(occ_mutex);
// if (r != 0)
// return sm_error_perm(SM_EM_OCC, r);
// }
// return SM_SUCCESS;
//
// enomem:
// ret = sm_error_temp(SM_EM_OCC, ENOMEM);
//
// /* clean up...? nothing to do right now */
// if (thr_unl_if_err(locktype))
// (void) smthread_mutex_unlock(occ_mutex);
// return ret;
//}
#endif /* 0 */
/*
** OCC_ENTRY_NEW -- get/create new OCC entry
**
** Parameters:
** occ_ht -- OCC hash table
** occ_fl_hd -- head of OCC freelist
** ipv4 -- IPv4 address of server (HACK)
** pocc_entry -- pointer to OCC entry (output)
** occ_mutex -- mutex to use for locking
** locktype -- kind of locking
**
** Returns:
** usual sm_error code; ENOMEM,
**
** Side Effects: none on error (except if unlock fails)
**
** Locking: locks occ_mutex if requested
**
** Last code review: 2005-03-17 05:12:52
** Last code change:
*/
sm_ret_T
occ_entry_new(OCC_HT_P occ_ht, occ_entry_hd_P occ_fl_hd, ipv4_T ipv4, occ_entry_P *pocc_entry, pthread_mutex_t *occ_mutex, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "occ_entry_new"
sm_ret_T ret;
int r;
bool allocated;
occ_entry_P occ_entry;
#if !DA_OCC_RSC
bht_entry_P bht_entry;
#endif
SM_REQUIRE(pocc_entry != NULL);
allocated = false;
if (thr_lock_it(locktype)) {
r = smthread_mutex_lock(occ_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_error_perm(SM_EM_OCC, r);
}
QM_LEV_DPRINTF(4, (QM_DEBFP, "func=occ_entry_new, empty=%d\n", OCCFL_EMPTY(occ_fl_hd)));
if (OCCFL_EMPTY(occ_fl_hd)) {
occ_entry = (occ_entry_P) sm_zalloc(sizeof(*occ_entry));
if (NULL == occ_entry) {
ret = sm_error_temp(SM_EM_OCC, ENOMEM);
goto error;
}
#if OCC_CHECK
occ_entry->sm_magic = SM_OCCE_MAGIC;
#endif
allocated = true;
}
else {
occ_entry = OCCFL_FIRST(occ_fl_hd);
SM_IS_OCCE(occ_entry);
OCCFL_REMOVE(occ_fl_hd);
}
occ_entry->occe_srv_ipv4 = ipv4; /* HACK! */
occ_entry->occe_init_conc = 0;
occ_entry->occe_cur_conc = 0;
occ_entry->occe_max_conc = 0;
occ_entry->occe_timeout = OCCE_TO;
QM_LEV_DPRINTF(4, (QM_DEBFP, "func=occ_entry_new, occ_entry=%p, ipv4=%A\n", occ_entry, (ipv4_T) ipv4));
#if DA_OCC_RSC
ret = rsc_add(occ_ht, true,
(char *)&occ_entry->occe_srv_ipv4,
sizeof(occ_entry->occe_srv_ipv4),
occ_entry, NULL, THR_NO_LOCK);
#else
ret = bht_add(occ_ht,
(char *)&occ_entry->occe_srv_ipv4,
sizeof(occ_entry->occe_srv_ipv4),
occ_entry, &bht_entry);
#endif
if (sm_is_err(ret)) {
QM_LEV_DPRINTF(4, (QM_DEBFP, "sev=ERROR, func=occ_entry_new, bht_add=%x", ret));
goto error;
}
*pocc_entry = occ_entry;
if (thr_unl_no_err(locktype)) {
r = smthread_mutex_unlock(occ_mutex);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_OCC, r);
}
return ret;
error:
/* clean up...? nothing to do right now */
if (thr_unl_if_err(locktype)) {
r = smthread_mutex_unlock(occ_mutex);
SM_ASSERT(r == 0);
}
if (allocated && occ_entry != NULL)
sm_free_size(occ_entry, sizeof(*occ_entry));
*pocc_entry = NULL; /* just a courtesy for the caller */
return ret;
}
/*
** OCC_ENTRY_FIND -- find OCC entry by IPv4 address
**
** Parameters:
** occ_ht -- OCC context
** ipv4 -- IPv4 address
** pocc_entry -- pointer to DA OCC entry (output)
** occ_mutex -- mutex to use for locking
** locktype -- kind of locking
**
** Returns:
** usual sm_error code; SM_E_NOTFOUND, (un)lock errors
**
** Side Effects: none on error (except if unlock fails)
** ok: may reset conc.conn. limit (if it is zero but timed out)
**
** Locking: locks occ_mutex if requested
**
** Last code review: 2005-03-17 05:14:35
** Last code change:
*/
sm_ret_T
occ_entry_find(OCC_HT_P occ_ht, ipv4_T ipv4, occ_entry_P *pocc_entry, pthread_mutex_t *occ_mutex, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "occ_entry_find"
sm_ret_T ret;
int r;
occ_entry_P occ_entry;
SM_REQUIRE(pocc_entry != NULL);
*pocc_entry = NULL; /* just a courtesy for the caller */
if (thr_lock_it(locktype)) {
r = smthread_mutex_lock(occ_mutex);
SM_LOCK_OK(r);
if (r != 0)
return sm_error_perm(SM_EM_OCC, r);
}
ret = SM_SUCCESS;
#if DA_OCC_RSC
occ_entry = (occ_entry_P) rsc_lookup(occ_ht,
(const char *)&ipv4, sizeof(ipv4), THR_NO_LOCK);
#else
occ_entry = (occ_entry_P) bht_find(occ_ht,
(const char *)&ipv4, sizeof(ipv4));
#endif
QM_LEV_DPRINTF(4, (QM_DEBFP, "func=occ_entry_find, entry=%p\n", occ_entry));
if (NULL == occ_entry)
ret = sm_error_perm(SM_EM_OCC, SM_E_NOTFOUND);
else {
SM_IS_OCCE(occ_entry);
/*
** If currently
#if 0
** less connections than initial connections
#else
** no connections
#endif
** are allowed and the entry timed out then reset it
** to the initial value.
** Should it be reset to a "reset" value (usually smaller
** than the initial value)?
** The initial value is "we don't know anything about this
** destination", the "reset" value would be: "it didn't work
** before, but let's try again".
**
** Should last_conn be used to "reset" an entry, i.e.,
** clear the number of open sessions?
** If the number gets out of sync then how should it
** recover? It could run some sanity check by looking
** at all dadb entries...
*/
if (
#if 0
occ_entry->occe_cur_conc < occ_entry->occe_init_conc
#else
occ_entry->occe_cur_conc == 0
#endif
&& occ_entry->occe_last_upd + occ_entry->occe_timeout <= time(NULLT))
occ_entry->occe_cur_conc = occ_entry->occe_init_conc;
*pocc_entry = occ_entry;
}
if ((!sm_is_err(ret) && thr_unl_no_err(locktype))
|| (sm_is_err(ret) && thr_unl_if_err(locktype)))
{
r = smthread_mutex_unlock(occ_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_OCC, r);
}
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1