/*
 * Copyright (c) 2004, 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: dadbclose.c,v 1.13 2007/05/28 17:15:37 ca Exp $")
#include "sm/types.h"
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/str.h"
#include "sm/mta.h"
#include "sm/memops.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "sm/dadb.h"
#include "sm/da.h"
#include "dadb.h"
#include "occ.h"

/*
**  DADB_ENTRY_FREE -- free a DADB entry
**
**	Parameters:
**		dadb_entry -- DADB entry
**
**	Returns:
**		none
**
**	Last code review: 2005-03-16 06:13:26
**	Last code change: 2007-05-28 14:56:52
*/

void
dadb_entry_free(dadb_entry_P dadb_entry)
{
	if (dadb_entry == NULL)
		return;
	SM_IS_DADBE(dadb_entry);
#if DADBE_MUTEX
	(void) pthread_mutex_destroy(&dadb_entry->dadbe_mutex);
#endif
#if DADB_CHECK
	dadb_entry->sm_magic = SM_MAGIC_NULL;
#endif
#if MTA_USE_TLS
	SM_STR_FREE(dadb_entry->dadbe_se_conf);
	SM_STR_FREE(dadb_entry->dadbe_lhs);
	SM_STR_FREE(dadb_entry->dadbe_tag);
#endif
	sm_free_size(dadb_entry, sizeof(*dadb_entry));
	return;
}

/*
**  DADB_CLOSE -- close a DADB; including all open sessions
**
**	Parameters:
**		qmgr_ctx -- QMGR context (only qmgr_conf is needed)
**		dadb_ctx -- DADB context
**		locktype -- kind of locking
**
**	Returns:
**		usual sm_error code
**
**	Locking: locks dadb_ctx if requested but only to set dadb_state.
**		may lock edbc_ctx, aq_ctx, dadb_ctx in subroutines.
**
**	Last code review: 2005-03-16 17:04:41; see comments below
**	Last code change: 2006-02-18 22:03:53
*/

sm_ret_T
dadb_close(qmgr_ctx_P qmgr_ctx, dadb_ctx_P dadb_ctx, thr_lock_T locktype)
{
	uint i;
	sm_ret_T ret;
	int r;
	bool done;
	dadb_entry_P dadb_entry;
	aq_ta_P aq_ta;

	if (dadb_ctx == NULL)
		return SM_SUCCESS;
	SM_IS_DADB(dadb_ctx);
	ret = SM_SUCCESS;
	if (thr_lock_it(locktype))
	{
		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
			return sm_error_perm(SM_EM_DA, r);
	}
	done = !DADB_IS_OK(dadb_ctx);
	if (!done)
		dadb_ctx->dadb_state = DADB_ST_STOP;

	/*
	**  Note: this should "wait" for any ongoing modifications to dadb.
	**  This could be achieved using a condition variable and a counter
	**  (for the number of ongoing dadb_entry modifications).
	**  A dadb_entry modification may be "in progress" but due to
	**  scheduling it may be done after this function "free"d dadb_entry.
	*/

	if (thr_unl_always(locktype))
	{
		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
		SM_ASSERT(r == 0);
		if (r != 0)
			ret = sm_error_perm(SM_EM_DA, r);
	}
	if (done)
		return ret;

	/*
	**  Notes:
	**  This loop checks dadb_ctx->dadb_entries != NULL to avoid an if
	**  around the entire loop (which requires further indentation).
	**  This isn't really optimal (checking the variable on each iteration)
	**  but shouldn't have any noticeable impact.
	**
	**  The return value (ret) of the last function call inside the loop
	**  is used as return value of this function; moreover, some return
	**  values are simply ignored. This isn't optimal but this is just
	**  some cleanup code hence any errors can be ignored.
	*/

	for (i = 0;
	     dadb_ctx->dadb_entries != NULL && i < dadb_ctx->dadb_entries_max;
	     i++)
	{
		dadb_entry = (dadb_ctx->dadb_entries)[i];
#if DADB_ENTRIES_DEBUG
		sm_io_fprintf(smioerr,
			"i=%3d, dadb_entry=%p, flags=%#x, da_se_id=%s, da_ta_id=%s\n"
			, i, dadb_entry
			, dadb_entry != NULL ? dadb_entry->dadbe_flags : 0xff
			, dadb_entry != NULL ? dadb_entry->dadbe_da_se_id : "-"
			, dadb_entry != NULL ? dadb_entry->dadbe_da_ta_id : "-"
			);
#endif
		if (dadb_entry == NULL)
			continue;
#if DADBE_MUTEX
		r = pthread_mutex_lock(&dadb_entry->dadbe_mutex);
		SM_LOCK_OK(r);
#endif
		if (!DADBE_IS_FREE(dadb_entry)) {
			if (dadb_entry->dadbe_rcpt != NULL &&
			    (aq_ta = dadb_entry->dadbe_rcpt->aqr_ss_ta) != NULL) {
					SM_ASSERT(SESSTA_EQ(dadb_entry->dadbe_ss_ta_id,
				                    aq_ta->aqt_ss_ta_id));
			}
			else
				ret = aq_ta_find(qmgr_ctx->qmgr_aq,
						dadb_entry->dadbe_ss_ta_id, true, &aq_ta);
			if (sm_is_success(ret)) {
				/* session must be closed */
				DADBE_SET_FLAG(dadb_entry, DADBE_FL_SE_CL);

				/* Update transaction status */
				ret = qda_update_ta_stat(qmgr_ctx,
					dadb_entry->dadbe_da_ta_id,
					SMTPC_DA_ST, DA_TERMINATED,
					dadb_ctx, dadb_entry,
					aq_ta, NULL, NULL, THR_LOCK_UNLOCK);
				/* fixme: complain on error? */
			}
			else {
				ret = dadb_sess_close_entry(qmgr_ctx, dadb_ctx,
					dadb_entry, true, NULL, THR_LOCK_UNLOCK);
			}
		}
#if DADBE_MUTEX
		r = pthread_mutex_unlock(&dadb_entry->dadbe_mutex);
		SM_ASSERT(r == 0);
#endif

		dadb_entry_free(dadb_entry);
		(dadb_ctx->dadb_entries)[i] = NULL;
	}
	FREE_DADB_ENTRIES(dadb_ctx);

	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1