/* * Copyright (c) 2002-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: edbreq.c,v 1.31 2006/12/29 01:28:09 ca Exp $") #include "sm/error.h" #include "sm/memops.h" #include "sm/heap.h" #include "sm/assert.h" #include "sm/str.h" #include "sm/edb.h" #include "edb-int.h" #include "sm/pthread.h" #include "log.h" /* DEBUGGING */ #include "sm/io.h" /* ** EDB_REQ_NEW -- get new edb request ** (from list of available edb requests or allocate a new one) ** ** Parameters: ** edb_ctx -- EDB context ** pedb_req -- edb req (output) ** flags -- flags (EDB_RQF_*) ** lockit -- lock edb_ctx? (reql_pool) ** ** Returns: ** usual sm_error code; ENOMEM, (un)lock ** ** Side Effects: none on error (except if unlock fails) ** ** Locking: locks edb_ctx if requested (note: don't use locktype because ** unlock on error can't work as unlock happens before an ** error may occur). ** ** Last code review: 2005-03-17 23:44:31 ** Last code change: 2005-03-17 23:17:09 */ sm_ret_T edb_req_new(edb_ctx_P edb_ctx, uint32_t flags, edb_req_P *pedb_req, bool lockit) { int r; sm_ret_T ret; edb_req_P edb_req; edb_req_hd_P edb_reql; SM_REQUIRE(pedb_req != NULL); if (lockit) { r = pthread_mutex_lock(&edb_ctx->edb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_EDB, r); } ret = SM_SUCCESS; #if SM_REQL_FLE if (SM_IS_FLAG(flags, EDB_RQF_EMERG)) edb_reql = &edb_ctx->edb_reql_fle; else #endif if (SM_IS_FLAG(flags, EDB_RQF_SMALL)) edb_reql = &edb_ctx->edb_reql_fls; else edb_reql = &edb_ctx->edb_reql_fln; if (!EDBREQL_EMPTY(edb_reql) && !SM_IS_FLAG(flags, EDB_RQF_ALLOC)) { edb_req = EDBREQL_FIRST(edb_reql); EDBREQL_REMOVE(edb_reql); if (lockit) { r = pthread_mutex_unlock(&edb_ctx->edb_mutex); SM_ASSERT(0 == r); if (r != 0) ret = sm_error_perm(SM_EM_EDB, r); } } else { if (lockit) { r = pthread_mutex_unlock(&edb_ctx->edb_mutex); SM_ASSERT(0 == r); if (r != 0) ret = sm_error_perm(SM_EM_EDB, r); } edb_req = (edb_req_P) sm_zalloc(sizeof(*edb_req)); if (NULL == edb_req) return sm_error_temp(SM_EM_EDB, ENOMEM); edb_req->edb_req_rcb = sm_rcb_new(NULL, SM_IS_FLAG(flags, EDB_RQF_SMALL) ? EDB_RC_SMALLSZ : EDB_RC_SZ, EDB_RC_MAXSZ); if (NULL == edb_req->edb_req_rcb) { sm_free_size(edb_req, sizeof(*edb_req)); return sm_error_temp(SM_EM_EDB, ENOMEM); } } *pedb_req = edb_req; return ret; } /* ** EDB_REQ_REL -- release edb request: clear out data, put it into pool ** ** Parameters: ** edb_ctx -- EDB context ** edb_req -- edb req ** flags -- flags (EDB_RQF_*) ** locktype -- kind of locking (edb_ctx (reql_pool)) ** ** Returns: ** SM_SUCCESS except for (un)lock errors ** ** Locking: check unlocking! XXX ** ** Last code review: 2005-03-23 19:58:11 ** Last code change: */ sm_ret_T edb_req_rel(edb_ctx_P edb_ctx, edb_req_P edb_req, uint32_t flags, thr_lock_T locktype) { int r; sm_ret_T ret; edb_req_hd_P edb_reql; if (NULL == edb_req) return SM_SUCCESS; if (SM_IS_FLAG(flags, EDB_RQF_FREE)) return edb_req_free(edb_req); SM_IS_EDB_CTX(edb_ctx); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&edb_ctx->edb_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_EDB, r); } ret = SM_SUCCESS; #if SM_REQL_FLE if (SM_IS_FLAG(flags, EDB_RQF_EMERG)) edb_reql = &edb_ctx->edb_reql_fle; else #endif if (sm_rcb_getsize(edb_req->edb_req_rcb) <= EDB_RC_SMALLSZ) edb_reql = &edb_ctx->edb_reql_fls; else edb_reql = &edb_ctx->edb_reql_fln; EDBREQL_PRE(edb_reql, edb_req); if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&edb_ctx->edb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_EDB, r); } return ret; } /* ** EDB_REQ_FREE -- free edb request ** ** Parameters: ** edb_req -- edb req ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-03-23 19:49:42 ** Last code change: */ sm_ret_T edb_req_free(edb_req_P edb_req) { if (NULL == edb_req) return SM_SUCCESS; if (edb_req->edb_req_rcb != NULL) sm_rcb_free(edb_req->edb_req_rcb); sm_free_size(edb_req, sizeof(*edb_req)); return SM_SUCCESS; } /* ** EDB_REQLS_FREE -- free edb request lists that are controlled by edb_ctx ** ** Parameters: ** edb_ctx -- EDB context ** ** Returns: ** SM_SUCCESS ** ** Side Effects: ** calls edb_wr_status() is wr list is not empty! ** ** Locking: ** edb_ctx (reql_wr, reql_pool) must be locked by caller ** ** Last code review: 2005-04-04 16:37:33 ** Last code change: 2005-04-04 16:37:30 */ sm_ret_T edb_reqls_free(edb_ctx_P edb_ctx) { edb_req_P edb_req; SM_IS_EDB_CTX(edb_ctx); /* This should be empty!!! */ if (!EDBREQL_EMPTY(&edb_ctx->edb_reql_wr)) { sm_log_write(edb_ctx->edb_lctx, EDB_LCAT_EDB, EDB_LMOD_EDB, SM_LOG_ERR, 4, "sev=ERROR, func=edb_reqls_free, edb_reql_wr=NOT_empty"); /* XXX Write list anyway? */ edb_wr_status(edb_ctx, NULL); } while (!EDBREQL_EMPTY(&edb_ctx->edb_reql_fln)) { edb_req = EDBREQL_FIRST(&edb_ctx->edb_reql_fln); EDBREQL_REMOVE(&edb_ctx->edb_reql_fln); edb_req_free(edb_req); } while (!EDBREQL_EMPTY(&edb_ctx->edb_reql_fls)) { edb_req = EDBREQL_FIRST(&edb_ctx->edb_reql_fls); EDBREQL_REMOVE(&edb_ctx->edb_reql_fls); edb_req_free(edb_req); } #if SM_REQL_FLE while (!EDBREQL_EMPTY(&edb_ctx->edb_reql_fle)) { edb_req = EDBREQL_FIRST(&edb_ctx->edb_reql_fle); EDBREQL_REMOVE(&edb_ctx->edb_reql_fle); edb_req_free(edb_req); } #endif return SM_SUCCESS; } /* ** EDB_REQL_FREE -- free edb request list (not put back in pool) ** ** Parameters: ** edb_ctx -- EDB context (unused) ** edb_req_hd -- head of request list for (DEF)EDB ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-03-23 19:55:35; see comments below ** Last code change: */ sm_ret_T edb_reql_free(edb_ctx_P edb_ctx, edb_req_hd_P edb_req_hd) { edb_req_P edb_req; SM_REQUIRE(edb_req_hd != NULL); while (!EDBREQL_EMPTY(edb_req_hd)) { /* ** Complain? Call function to write this out? ** Add a parameter which indicates whether this is ** a "cancel" (just free the data) or a write+free? */ edb_req = EDBREQL_FIRST(edb_req_hd); EDBREQL_REMOVE(edb_req_hd); edb_req_free(edb_req); } return SM_SUCCESS; }