/*
 * 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;
}


syntax highlighted by Code2HTML, v. 0.9.1