/* * Copyright (c) 2003-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: ssocc.c,v 1.22 2007/10/25 01:42:30 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/bhtable.h" #include "sm/ssocc.h" #include "sm/qmgr-int.h" /* ** SSOCC_CLOSE -- close a SSOCC_CTX ** ** Parameters: ** ssocc_ctx -- SSOCC context ** ** Returns: ** usual sm_error code ** ** Locking: no locking, destroys SSOCC (and hence lock) */ sm_ret_T ssocc_close(ssocc_ctx_P ssocc_ctx) { if (ssocc_ctx == NULL) return SM_SUCCESS; SM_IS_SSOCC(ssocc_ctx); #if SSOCC_RSC rsc_free(ssocc_ctx->ssocc_ht); #else bht_destroy(ssocc_ctx->ssocc_ht, NULL, NULL); #endif sm_connctl_free(ssocc_ctx->ssocc_cctx); (void) pthread_mutex_destroy(&ssocc_ctx->ssocc_mutex); sm_free_size(ssocc_ctx, sizeof(*ssocc_ctx)); return SM_SUCCESS; } /* ** SSOCC_OPEN -- open a new SSOCC ** ** Parameters: ** pssocc_ctx -- pointer to SSOCC context (output) ** size -- maximum size of SSOCC ** connctl_size -- size of connection control table ** ** Returns: ** usual sm_error code */ sm_ret_T ssocc_open(ssocc_ctx_P *pssocc_ctx, uint size, uint connctl_size) { int r; sm_ret_T ret; ssocc_ctx_P ssocc_ctx; SM_REQUIRE(pssocc_ctx != NULL); ssocc_ctx = (ssocc_ctx_P) sm_zalloc(sizeof(*ssocc_ctx)); if (ssocc_ctx == NULL) { ret = sm_error_temp(SM_EM_Q, ENOMEM); goto error; } #if SSOCC_RSC ssocc_ctx->ssocc_ht = rsc_new(size * 2, size * 3, rsc_occe_create, rsc_occe_delete, (void *) ssocc_ctx); #else ssocc_ctx->ssocc_ht = bht_new(size, size * 2); #endif if (ssocc_ctx->ssocc_ht == NULL) { ret = sm_error_temp(SM_EM_Q, ENOMEM); goto errfree; } OCCFL_INIT(&ssocc_ctx->ssocc_fl_hd); /* CONF constants? configuration parameters? */ ret = sm_connctl_new(connctl_size, 5, &ssocc_ctx->ssocc_cctx); if (sm_is_err(ret)) goto errfree; r = pthread_mutex_init(&ssocc_ctx->ssocc_mutex, SM_PTHREAD_MUTEXATTR); if (r != 0) { ret = sm_error_perm(SM_EM_Q, r); goto errfree; } #if SSOCC_CHECK ssocc_ctx->sm_magic = SM_SSOCC_MAGIC; #endif *pssocc_ctx = ssocc_ctx; return SM_SUCCESS; errfree: if (ssocc_ctx != NULL) { #if SSOCC_RSC rsc_free(ssocc_ctx->ssocc_ht); #else bht_destroy(ssocc_ctx->ssocc_ht, NULL, NULL); #endif sm_connctl_free(ssocc_ctx->ssocc_cctx); sm_free_size(ssocc_ctx, sizeof(*ssocc_ctx)); } error: return ret; } /* ** SSOCC_ENTRY_FREE -- free SSOCC entry (return it to free-list) ** ** Parameters: ** ssocc_ctx -- SSOCC context ** ssocc_entry -- SSOCC entry ** locktype -- kind of locking ** ** Returns: ** usual sm_error code ** ** Locking: check unlocking! XXX */ sm_ret_T ssocc_entry_free(ssocc_ctx_P ssocc_ctx, ssocc_entry_P ssocc_entry, thr_lock_T locktype) { int r; SM_IS_SSOCC(ssocc_ctx); if (ssocc_entry == NULL) return SM_SUCCESS; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ssocc_ctx->ssocc_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_Q, r); } SM_IS_SSOCCE(ssocc_entry); #if SSOCC_RSC (void) rsc_rm(ssocc_ctx->ssocc_ht, (char *)&ssocc_entry->ssocce_srv_ipv4, sizeof(ssocc_entry->ssocce_srv_ipv4), THR_NO_LOCK); #else /* SSOCC_RSC */ (void) bht_rm(ssocc_ctx->ssocc_ht, (char *)&ssocc_entry->ssocce_srv_ipv4, sizeof(ssocc_entry->ssocce_srv_ipv4), NULL, NULL); #endif /* SSOCC_RSC */ ssocc_entry->ssocce_srv_ipv4 = 0; ssocc_entry->ssocce_open_se = 0; ssocc_entry->ssocce_open_se_exc = 0; ssocc_entry->ssocce_last_conn = 0; SSOCCFL_PRE(&ssocc_ctx->ssocc_fl_hd, ssocc_entry); /* XXX free entry if there are too many? */ /* do not reset ssocc_entry->sm_magic, the entry can be reused */ if (thr_unl_always(locktype)) { r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); if (r != 0) { QM_LEV_DPRINTF(1, (QM_DEBFP, "sev=ERROR, func=ssocc_entry_free, unlock=%d\n", r)); /* XXX ??? COMPLAIN */ return sm_error_perm(SM_EM_Q, r); } } return SM_SUCCESS; } #if 0 /* ** SSOCC_ENTRY_GET -- get/create new SSOCC entry ** ** Parameters: ** ssocc_ctx -- SSOCC context ** pssocc_entry -- pointer to SSOCC entry (output) ** locktype -- kind of locking ** ** Returns: ** usual sm_error code */ sm_ret_T ssocc_entry_get(ssocc_ctx_P ssocc_ctx, ssocc_entry_P *pssocc_entry, thr_lock_T locktype) { sm_ret_T ret; int r; ssocc_entry_P ssocc_entry; SM_IS_SSOCC(ssocc_ctx); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ssocc_ctx->ssocc_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_Q, r); } if (SSOCCFL_EMPTY(&ssocc_ctx->ssocc_fl_hd)) { ssocc_entry = (ssocc_entry_P) sm_zalloc(sizeof(*ssocc_entry)); if (ssocc_entry == NULL) goto enomem; #if SSOCC_CHECK ssocc_entry->sm_magic = SM_SSOCCE_MAGIC; #endif } else { ssocc_entry = SSOCCFL_FIRST(&ssocc_ctx->ssocc_fl_hd); SM_IS_SSOCCE(ssocc_entry); SSOCCFL_REMOVE(&ssocc_ctx->ssocc_fl_hd); } *pssocc_entry = ssocc_entry; if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); if (r != 0) { /* XXX ??? COMPLAIN */ return sm_error_perm(SM_EM_Q, r); } } return SM_SUCCESS; enomem: ret = sm_error_temp(SM_EM_Q, ENOMEM); /* clean up...? nothing to do right now */ if (thr_unl_if_err(locktype)) (void) pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); return ret; } #endif /* 0 */ /* ** SSOCC_ENTRY_NEW -- get/create new SSOCC entry ** ** Parameters: ** ssocc_ctx -- SSOCC context ** ipv4 -- IPv4 address of server (HACK) ** pssocc_entry -- pointer to SSOCC entry (output) ** locktype -- kind of locking ** ** Returns: ** usual sm_error code */ sm_ret_T ssocc_entry_new(ssocc_ctx_P ssocc_ctx, ipv4_T ipv4, ssocc_entry_P *pssocc_entry, thr_lock_T locktype) { sm_ret_T ret; int r; ssocc_entry_P ssocc_entry; #if !SSOCC_RSC bht_entry_P bht_entry; #endif SM_IS_SSOCC(ssocc_ctx); ssocc_entry = NULL; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ssocc_ctx->ssocc_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_Q, r); } QM_LEV_DPRINTF(5, (QM_DEBFP, "func=ssocc_entry_new, empty=%d\n", SSOCCFL_EMPTY(&ssocc_ctx->ssocc_fl_hd))); if (SSOCCFL_EMPTY(&ssocc_ctx->ssocc_fl_hd)) { ssocc_entry = (ssocc_entry_P) sm_zalloc(sizeof(*ssocc_entry)); if (ssocc_entry == NULL) goto enomem; #if SSOCC_CHECK ssocc_entry->sm_magic = SM_SSOCCE_MAGIC; #endif } else { ssocc_entry = SSOCCFL_FIRST(&ssocc_ctx->ssocc_fl_hd); SM_IS_SSOCCE(ssocc_entry); SSOCCFL_REMOVE(&ssocc_ctx->ssocc_fl_hd); } ssocc_entry->ssocce_srv_ipv4 = ipv4; /* XXX HACK! */ #if 0 ssocc_entry->ssocce_init_conc = 0; ssocc_entry->ssocce_cur_conc = 0; ssocc_entry->ssocce_max_conc = 0; #endif QM_LEV_DPRINTF(4, (QM_DEBFP, "func=ssocc_entry_new, ssocc_entry=%p, ipv4=%A\n", ssocc_entry, (ipv4_T) ipv4)); #if SSOCC_RSC ret = rsc_add(ssocc_ctx->ssocc_ht, true, (char *)&ssocc_entry->ssocce_srv_ipv4, sizeof(ssocc_entry->ssocce_srv_ipv4), ssocc_entry, NULL, THR_NO_LOCK); #else /* SSOCC_RSC */ ret = bht_add(ssocc_ctx->ssocc_ht, (char *)&ssocc_entry->ssocce_srv_ipv4, sizeof(ssocc_entry->ssocce_srv_ipv4), ssocc_entry, &bht_entry); #endif /* SSOCC_RSC */ if (sm_is_err(ret)) { QM_LEV_DPRINTF(1, (QM_DEBFP, "sev=ERROR, func=ssocc_entry_new, bht_add=%x\n", ret)); goto error; } *pssocc_entry = ssocc_entry; if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); if (r != 0) { QM_LEV_DPRINTF(1, (QM_DEBFP, "sev=ERROR, func=ssocc_entry_new, unlock=%d", r)); /* XXX ??? COMPLAIN */ return sm_error_perm(SM_EM_Q, r); } } return ret; enomem: ret = sm_error_temp(SM_EM_Q, ENOMEM); error: if (ssocc_entry != NULL) { /* ** Don't call ssocc_entry_free() because it calls bht_rm() ** but the entry hasn't been added (that's the error cause!) ** Maybe free() the entry instead (esp. if it was allocated)? */ ssocc_entry->ssocce_srv_ipv4 = 0; ssocc_entry->ssocce_open_se = 0; ssocc_entry->ssocce_open_se_exc = 0; ssocc_entry->ssocce_last_conn = 0; SSOCCFL_PRE(&ssocc_ctx->ssocc_fl_hd, ssocc_entry); ssocc_entry = NULL; } /* clean up...? nothing to do right now */ if (thr_unl_if_err(locktype)) { (void) pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); QM_LEV_DPRINTF(2, (QM_DEBFP, "func=ssocc_entry_new, err_unlock\n")); } return ret; } /* ** SSOCC_ENTRY_FIND -- find a session in SSOCC by IPv4 address ** ** Parameters: ** ssocc_ctx -- SSOCC context ** ipv4 -- IPv4 address ** pssocc_entry -- pointer to SSOCC entry (output) ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; SM_E_NOTFOUND, (un)lock errors */ sm_ret_T ssocc_entry_find(ssocc_ctx_P ssocc_ctx, ipv4_T ipv4, ssocc_entry_P *pssocc_entry, thr_lock_T locktype) { sm_ret_T ret; int r; ssocc_entry_P ssocc_entry; SM_IS_SSOCC(ssocc_ctx); SM_REQUIRE(pssocc_entry != NULL); *pssocc_entry = NULL; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ssocc_ctx->ssocc_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_Q, r); } ret = SM_SUCCESS; #if SSOCC_RSC ssocc_entry = (ssocc_entry_P) rsc_lookup(ssocc_ctx->ssocc_ht, (const char *)&ipv4, sizeof(ipv4), THR_NO_LOCK); #else ssocc_entry = (ssocc_entry_P) bht_find(ssocc_ctx->ssocc_ht, (const char *)&ipv4, sizeof(ipv4)); #endif QM_LEV_DPRINTF(5, (QM_DEBFP, "func=ssocc_entry_find, entry=%p\n", ssocc_entry)); if (ssocc_entry == NULL) { ret = sm_error_perm(SM_EM_Q, SM_E_NOTFOUND); goto error; } else { SM_IS_SSOCCE(ssocc_entry); #if 0 if (ssocc_entry->ssocce_cur_conc == 0 && ssocc_entry->ssocce_last_conn + SSOCCE_TO < time(NULLT)) ssocc_entry->ssocce_cur_conc = ssocc_entry->ssocce_init_conc; #endif /* 0 */ *pssocc_entry = ssocc_entry; } if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); QM_LEV_DPRINTF(1, (QM_DEBFP, "func=ssocc_entry_find, unlock=%d\n", r)); if (r != 0) { /* XXX ??? COMPLAIN */ return sm_error_perm(SM_EM_Q, r); } } return ret; error: /* clean up...? nothing to do right now */ if (thr_unl_if_err(locktype)) { (void) pthread_mutex_unlock(&ssocc_ctx->ssocc_mutex); QM_LEV_DPRINTF(1, (QM_DEBFP, "func=ssocc_entry_find, err_unlock\n")); } return ret; }