/*
* Copyright (c) 2002-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.
*
* $Id: qmgr-int.h,v 1.215 2007/05/05 04:52:30 ca Exp $
*/
#ifndef SM_QMGR_INT_H
#define SM_QMGR_INT_H 1
#include "sm/generic.h"
#include "sm/magic.h"
#include "sm/pthread.h"
#include "sm/note.h"
#include "sm/heap.h"
#include "sm/net.h"
#include "sm/io.h"
#include "sm/evthr.h"
#include "sm/rcb.h"
#include "sm/rcbl.h"
#include "sm/rcbcomm.h"
#include "sm/iqdb.h"
#include "sm/mta.h"
#include "sm/ibdb.h"
#include "sm/qmgrcto.h"
#include "sm/qmgrcomm.h"
#include "sm/qmgr.h"
#include "sm/cdb.h"
#include "sm/actdb-int.h"
#include "sm/dadb.h"
#include "sm/edb.h"
#include "sm/edbc.h"
#include "sm/ssoccstr.h"
#include "sm/occstr.h"
#include "sm/log.h"
#include "sm/qmgrdef.h"
#include "sm/qmgrdbg.h"
#include "sm/qmgrcnf.h"
#include "sm/fs.h"
#include "sm/cdbfs.h"
#include "sm/edbfs.h"
#include "sm/maps.h"
#include "sm/map.h"
#if SM_HEAP_CHECK
extern SM_DEBUG_T SmHeapCheck;
# define HEAP_CHECK (SmHeapCheck > 0)
#else
# define HEAP_CHECK 0
#endif
/* abstraction layer for QMGR */
#if 0
incedb_open(IN name, IN mode, IN size, OUT status, OUT incedb-handle)
open an incoming envelope database.
incedb_close(IN incedb-handle, OUT status)
close an incoming envelope database.
incedb_session_new(IN incedb-handle, IN session-info, OUT status, OUT session-id):
create a new session (client connects succesfully).
incedb_trans_new(IN incedb-handle, IN sender-env-info, OUT status, OUT trans-id):
create a new envelope (done on MAIL command).
incedb_rcpt_add(IN incedb-handle, IN trans-id, IN rcpt-env-info, OUT status):
add a new recipient (RCPT command).
incedb_rcpt_rm(IN incedb-handle, IN trans-id, IN rcpt-id, IN rpct-status,
OUT status):
remove a recipient (mail has been taken care of).
This includes a recipient status,
e.g., successfully delivered, some kind of failure,
and hence the recipient ended up in another queue.
The caller will take care of DSN handling or putting the entry into
another queue if necessary.
incedb_trans_rm(IN incedb-handle, IN trans-id, OUT status):
remove an envelope from the EDB (after all recipients have been taken care of).
Should this be done implicitly when all recipients have been removed?
incedb_commit(IN incedb-handle, IN trans-id, OUT status):
commit envelope information to stable storage.
incedb_session_rm(IN incedb-handle, IN session-id, OUT status):
remove a session from the EDB.
incedb_trans_discard(IN incedb-handle, IN trans-id, OUT status):
discard envelope (connection has been aborted somehow:
RSET, EHLO, connection error on lower level).
incedb_items_cache(IN incedb-handle, OUT items, OUT status):
return the number of items in the memory cache.
incedb_readprep(IN incedb-handle, OUT cursor, OUT status):
prepare to read through EDB.
incedb_getnext(IN cursor, OUT status, OUT record):
Get next entry from EDB.
incedb_readclose(IN incedb-handle, IN cursor, OUT status):
stop reading through EDB.
#endif /* 0 */
#define SM_IS_QMGR_CTX(qmgr_ctx) SM_REQUIRE_ISA((qmgr_ctx), SM_QMGR_CTX_MAGIC)
#define SM_IS_QSS_CTX(qss_ctx) SM_REQUIRE_ISA((qss_ctx), SM_QSS_CTX_MAGIC)
#define SM_IS_QSC_CTX(qsc_ctx) SM_REQUIRE_ISA((qsc_ctx), SM_QSC_CTX_MAGIC)
#define SM_IS_QAR_CTX(qar_ctx) SM_REQUIRE_ISA((qar_ctx), SM_QAR_CTX_MAGIC)
/* HACK .... */
#define MAX_GEN_LI_FD 32 /* must <= 32, see qm_gli_used */
/* used to be: SM_ARRAY_SIZE((qmgr_ctx)->qmgr_gli[QM_SMTPS_GLI].qm_gli_ctx) */
#define QM_N_GLI(qmgr_ctx) MAX_GEN_LI_FD
#define QM_N_SS_GLI(qmgr_ctx) MAX_GEN_LI_FD
#define QM_N_SC_GLI(qmgr_ctx) MAX_GEN_LI_FD
extern cdb_id_P null_cdb_id;
/* QMGR resource flags */
#define QMGR_RFL_NONE 0x00000000U
/*
** Slow down due to ...
** Notice: this is used in two ways:
** 1. it's the shift value (1 << FL_x_I)
** 2. it's the index into arrays that contain limits, current values, etc.
** see qmgr_ctx: qmgr_usage[], qmgr_lower[], qmgr_upper[]
*/
#define IDX2RFL(fl) ((uint32_t)1 << (fl))
#define QMGR_RFL_AQ_I 0u /* AQ */
#define QMGR_RFL_IQD_I 1u /* IQDB */
#define QMGR_RFL_IBD_I 2u /* IBDB disk space */
#define QMGR_RFL_MEM_I 3u /* MEM */
#define QMGR_RFL_CPU_I 4u /* CPU */
#define QMGR_RFL_EBDC_I 5u /* EDBC */
#define QMGR_RFL_AR_I 6u /* AR */
#define QMGR_RFL_CDB_I 7u /* CDB disk space */
#define QMGR_RFL_EDB_I 8u /* EDB disk space */
#define QMGR_RFL_LAST_I 8u /* last index */
/* ... more? */
#define QMGR_RFL_AQ IDX2RFL(QMGR_RFL_AQ_I)
#define QMGR_RFL_IQD IDX2RFL(QMGR_RFL_IQD_I)
#define QMGR_RFL_IBD IDX2RFL(QMGR_RFL_IBD_I)
#define QMGR_RFL_MEM IDX2RFL(QMGR_RFL_MEM_I)
#define QMGR_RFL_CPU IDX2RFL(QMGR_RFL_CPU_I)
#define QMGR_RFL_EBDC IDX2RFL(QMGR_RFL_EBDC_I)
#define QMGR_RFL_AR IDX2RFL(QMGR_RFL_AR_I)
#define QMGR_RFL_CDB IDX2RFL(QMGR_RFL_CDB_I)
#define QMGR_RFL_EDB IDX2RFL(QMGR_RFL_EDB_I)
/* all resources */
#define QMGR_RFL_ALL_RSR (QMGR_RFL_AQ|QMGR_RFL_IQD|QMGR_RFL_IBD|QMGR_RFL_MEM|QMGR_RFL_CPU|QMGR_RFL_EBDC|QMGR_RFL_AR|QMGR_RFL_CDB|QMGR_RFL_EDB)
#define QMGR_SET_RFLAG_I(qmgr_ctx, idx) (qmgr_ctx)->qmgr_rflags |= IDX2RFL(idx)
#define QMGR_CLR_RFLAG_I(qmgr_ctx, idx) (qmgr_ctx)->qmgr_rflags &= ~IDX2RFL(idx)
#define QMGR_IS_RFLAG_I(qmgr_ctx, idx) (((qmgr_ctx)->qmgr_rflags & IDX2RFL(idx)) != 0)
/* is any other resource (flag) than idx "used" (set)? */
#define QMGR_RFL_ANY_RSR_BUT_I(qmgr_ctx, idx) (((qmgr_ctx)->qmgr_rflags & (QMGR_RFL_ALL_RSR^IDX2RFL(idx))) != 0)
#define QMGR_RFL_IS_NO_RSR(qmgr_ctx) (((qmgr_ctx)->qmgr_rflags & QMGR_RFL_ALL_RSR) == 0)
#define QMGR_SET_RFLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_rflags |= (fl)
#define QMGR_CLR_RFLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_rflags &= ~(fl))
#define QMGR_IS_RFLAG(qmgr_ctx, fl) (((qmgr_ctx)->qmgr_rflags & (fl)) != 0)
#define QMGR_R_USE_NONE 0 /* resource is completely unused */
#define QMGR_R_USE_FULL 100 /* resource is completely used */
#define DISK_USAGE(x, qmgr_ctx) \
(((x) >= (qmgr_ctx)->qmgr_cnf.q_cnf_ok_df) ? QMGR_R_USE_NONE : \
(((x) <= (qmgr_ctx)->qmgr_cnf.q_cnf_min_df) ? QMGR_R_USE_FULL : \
((((qmgr_ctx)->qmgr_cnf.q_cnf_ok_df - (x)) * 100) / \
((qmgr_ctx)->qmgr_cnf.q_cnf_ok_df - (qmgr_ctx)->qmgr_cnf.q_cnf_min_df))))
/* QMGR status flags */
#define QMGR_SFL_NONE 0x00000000U
#define QMGR_SFL_EDBC 0x00000001U /* EDBC is full (not checked anywhere?) */
#define QMGR_SFL_AR 0x00000002U /* AR is unavailable */
#define QMGR_SFL_DA 0x00000004U /* no DA available */
#define QMGR_SFL_HAD_DA 0x00000008U /* DA was once available */
#define QMGR_SET_SFLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_sflags |= (fl)
#define QMGR_CLR_SFLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_sflags &= ~(fl)
#define QMGR_IS_SFLAG(qmgr_ctx, fl) (((qmgr_ctx)->qmgr_sflags & (fl)) != 0)
/* XXX HACK XXX */
#define IBDB_NAME "ibd"
#define QCNF_SET_FLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_cnf.q_cnf_flags |= (fl)
#define QCNF_CLR_FLAG(qmgr_ctx, fl) (qmgr_ctx)->qmgr_cnf.q_cnf_flags &= ~(fl)
#define QCNF_IS_FLAG(qmgr_ctx, fl) (((qmgr_ctx)->qmgr_cnf.q_cnf_flags & (fl)) != 0)
#if QMGR_TEST
/*
** Values to trigger errors in certain places to test error handling.
** Currently these are used as bitflags which allows for combining them.
** Alternatively numbers might be used if there are many tests that don't
** need to be combined.
*/
# define QMGR_TEST_BNC_FAIL 0x00000001 /* cause a bounce failure */
/* cause a failure if rcpt_status is 459 */
# define QMGR_TEST_RCPT_STAT 0x00000002
# define QMGR_TEST_RSR_ST 459
/* cause a failure in qda_upd_dsn() */
# define QMGR_TEST_UPD_DSN 0x00000004
/* cause a failure in qm_bounce_new() at aq_rcpt_add_new() */
# define QMGR_TEST_BNC_RCPT_ADD 0x00000008
/* cause a failure in qm_bounce_new() at allocating aqr_dsns */
# define QMGR_TEST_ALLOC_DSNS 0x00000010
/* cause an abort in qda_update_ta_stat() */
# define QMGR_TEST_UPD_ABORT 0x00000020
/* cause an abort in sched_dlvry() for a "delayed" DSN */
# define QMGR_TEST_DLY_SCHED 0x00000040
/* cause an error in q_store_ddsn() for a "delayed" DSN */
# define QMGR_TEST_DDSN 0x00000080
/* cause a communication error (reading) with smtpc */
# define QMGR_TEST_SC_RD 0x00000100
/* delay start of scheduler */
# define QMGR_TEST_SCHED_DLY 0x00000200
/* use internal source of transactions */
# define QMGR_TEST_INT_SRC 0x00000400
#endif
/* generic description for qmgr to maintain client data (smtps/smtpc) */
typedef sm_ret_T (qmgr_smtp_F)(sm_evthr_task_P _tsk);
struct qm_gli_S
{
sm_magic_T sm_magic;
qmgr_ctx_P qm_gli_qmgr_ctx; /* pointer back to main ctx */
int qm_gli_lfd; /* listen fd */
int qm_gli_nfd; /* number of used fds */
uint32_t qm_gli_used; /* bitmask for used elements */
/* communication function to start as task */
qmgr_smtp_F *qm_gli_fct;
/* client contexts to use for each connected client */
qsg_ctx_P qm_gli_ctx[MAX_GEN_LI_FD];
};
#define SM_IS_GLI(qm_gli) SM_REQUIRE_ISA((qm_gli), SM_GLI_MAGIC)
/*
** QMGR main context type
*/
struct qmgr_ctx_S
{
sm_magic_T sm_magic;
pthread_mutex_t qmgr_mutex;
uint qmgr_status; /* see below, QMGR_ST_* */
time_T qmgr_st_time; /* start time */
/*
** XXX more status information?
** e.g., how full are the various queues, load, memory usage,
** have some subcomponents been slowed down, ...
*/
/* Resource flags */
uint32_t qmgr_rflags; /* see QMGR_RFL_* */
/* Overall value to indicate resource usage 0:free 100:overloaded */
uint qmgr_total_usage;
/* Status flags */
uint32_t qmgr_sflags; /* see QMGR_SFL_* */
sm_str_P qmgr_hostname;
sm_str_P qmgr_pm_addr; /* <postmaster@hostname> */
cdb_ctx_P qmgr_cdb_ctx;
/* info about connections? */
fs_ctx_P qmgr_fs_ctx;
cdb_fsctx_P qmgr_cdb_fsctx;
ulong qmgr_cdb_kbfree;
edb_fsctx_P qmgr_edb_fsctx;
ulong qmgr_edb_kbfree;
ulong qmgr_ibdb_kbfree;
/* generic listeners (currently only for smtpc/smtps) */
qm_gli_T qmgr_gli[2];
#define QM_SMTPS_GLI 0
#define QM_SMTPC_GLI 1
#define qmgr_ss_li qmgr_gli[QM_SMTPS_GLI]
#define qmgr_sc_li qmgr_gli[QM_SMTPC_GLI]
#define qmgr_li_ss(qmgr_ctx, i) ((qss_ctx_P) (qmgr_ctx)->qmgr_ss_li.qm_gli_ctx[i])
#define qmgr_li_sc(qmgr_ctx, i) ((qsc_ctx_P) (qmgr_ctx)->qmgr_sc_li.qm_gli_ctx[i])
#define qmgr_li_ss_lv(qmgr_ctx, i) (qmgr_ctx)->qmgr_ss_li.qm_gli_ctx[i]
#define qmgr_li_sc_lv(qmgr_ctx, i) (qmgr_ctx)->qmgr_sc_li.qm_gli_ctx[i]
id_count_T qmgr_idc; /* last used SMTP id counter */
/* SMTPS */
ssocc_ctx_P qmgr_ssocc_ctx;
/* SMTPC */
occ_ctx_P qmgr_occ_ctx;
/* total number of DAs (delivery threads) */
ulong qmgr_max_da_threads;
uint qmgr_tmo_sched;
time_T qmgr_tm_no_da; /* last time of "no DA avail" */
/* control socket */
int qmgr_ctllfd; /* listen fd */
int qmgr_ctlfd; /* control socket fd */
rcbcom_ctx_T qmgr_ctl_com;
sm_evthr_ctx_P qmgr_ev_ctx; /* event thread context */
iqdb_P qmgr_iqdb; /* rsc for incoming edb */
ibdb_ctx_P qmgr_ibdb; /* backup for incoming edb */
sm_evthr_task_P qmgr_icommit; /* task for ibdbc commits */
qss_opta_P qmgr_optas; /* open transactions (commit) */
sm_evthr_task_P qmgr_sched; /* scheduling task */
aq_ctx_P qmgr_aq; /* active envelope db */
edb_ctx_P qmgr_edb; /* deferred envelope db */
edbc_ctx_P qmgr_edbc; /* cache for envelope db */
sm_evthr_task_P qmgr_tsk_cleanup; /* task for cleanup */
qcleanup_ctx_P qmgr_cleanup_ctx;
sm_maps_P qmgr_maps; /* map system context */
sm_map_P qmgr_conf_map; /* "configuration" map */
/* AR */
sm_evthr_task_P qmgr_ar_tsk; /* address resolver task */
int qmgr_ar_fd; /* communication fd */
qar_ctx_P qmgr_ar_ctx;
sm_rcbh_T qmgr_rcbh; /* head for RCB list */
uint qmgr_rcbn; /* number of entries in RCB list */
/* currently protected by qmgr_mutex */
/* Maybe use _P instead? */
qmgr_cnf_T qmgr_cnf;
sm_log_ctx_P qmgr_lctx;
sm_logconfig_P qmgr_lcfg;
uint8_t qmgr_usage[QMGR_RFL_LAST_I + 1];
uint8_t qmgr_lower[QMGR_RFL_LAST_I + 1];
uint8_t qmgr_upper[QMGR_RFL_LAST_I + 1];
#if QMGR_STATS
uint8_t qmgr_max_use[QMGR_RFL_LAST_I + 1];
/* some statistics */
ulong qmgr_rcpts_rcvd;
ulong qmgr_tas_rcvd;
ulong qmgr_rcpts_sent;
ulong qmgr_tas_sent;
#endif /* QMGR_STATS */
#if EVTHR_DEBUG
uint qmgr_evthr_dbg_lvl;
#endif
};
NOTE(MUTEX_PROTECTS_DATA(qmgr_ctx_S::qmgr_mutex, qmgr_ctx_S::))
#if QMGR_STATS
#define QMGR_SET_USE_MAX(resource) do { \
if (qmgr_ctx->qmgr_usage[resource] > qmgr_ctx->qmgr_max_use[resource]) \
qmgr_ctx->qmgr_max_use[resource] = qmgr_ctx->qmgr_usage[resource]; \
} while (0)
#else
#define QMGR_SET_USE_MAX(resource) SM_NOOP
#endif
/*
** XXX do we want to dynamically create these or do we want to
** preallocate them in qmgr_ctx? The former is more flexible, the
** latter 'safer' (can't fail during operation). If we limit the
** number of connections then we may as well preallocate the data.
** Note: limiting might be used as a DoS attack as well as unlimited;
** the former to prevent legitimate connections, the latter to exhaust
** memory. It would be very useful to restrict access to the communication
** channel to "authorized" programs (Unix sockets: permissions, others?).
** However, if some people want to run "many" SMTPS (instead of relying
** on threads), then a list would be more useful.
*/
typedef uint8_t qss_status_T;
typedef uint8_t qsc_status_T;
typedef uint8_t qar_status_T;
/* generic data shared between qss_ctx_S and qsc_ctx_S */
struct qsm_gen_S
{
sm_magic_T sm_magic;
rcbcom_ctx_T qsm_gen_com;
qmgr_ctx_P qsm_gen_qmgr_ctx; /* pointer back to main ctx */
int qsm_gen_id; /* id */
uint32_t qsm_gen_bit; /* bit for q_smtp_used */
qss_status_T qsm_gen_status; /* status */
};
/*
** Task context QMGR/SMTPS
** Generally access to this structure is restricted since it is associated
** with exactly one fd and hence one (evthr) task.
** However, see below for details which parts are protected by mutexes.
** Note: the task for the qss context is available as:
** qss_ctx->qss_com.rcbcom_tsk
*/
struct qss_ctx_S
{
sm_magic_T sm_magic;
rcbcom_ctx_T qss_com;
qmgr_ctx_P qss_qmgr_ctx; /* pointer back to main ctx */
int qss_id; /* SMTPS id, must be signed (QSS_ID_NONE) */
uint32_t qss_bit; /* bit for qmgr_ssctx */
qss_status_T qss_status; /* status of SMTPS */
/* more data ... see docs? */
sessta_id_T qss_ta_id; /* last SMTPS TA id */
#if MTA_USE_SMTPS_MUTEX
pthread_mutex_t qss_mutex;
#endif
uint qss_max_thrs; /* upper limit for threads */
uint qss_max_cur_thrs; /* current limit for threads */
uint qss_cur_session; /* current # of sessions */
#if QMGR_STATS
uint qss_max_session; /* max # of sessions */
#endif
};
/*
** Task context QMGR/SMTPC
**
** Items that can be changed during runtime are:
** qsc_status
** qsc_curactive protected by qsc_mutex
** qsc_maxthreads protected by qsc_mutex
** qsc_id_cnt protected by dadb, see qmgr/id.c
*/
struct qsc_ctx_S
{
sm_magic_T sm_magic;
rcbcom_ctx_T qsc_com;
qmgr_ctx_P qsc_qmgr_ctx; /* pointer back to main ctx */
int qsc_id; /* SMTPC id (set by smtpc) */
uint32_t qsc_bit; /* bit for qmgr_scctx */
/* split this in status and flags? */
qsc_status_T qsc_status; /* status of SMTPC */
uint8_t qsc_idx; /* SMTPC index (set by qmgr) */
dadb_ctx_P qsc_dadb_ctx; /* pointer to DA DB context */
/* more data ... see docs? */
#if 0
/*
** Also in dadb_ctx. Do we need two different data structures?
** Can SMTPC have multiple DAs? Currently no...
** So why not merge these two structures into one?
** Maybe the other way around? Several SMTPC can implement
** the same DA, hence they should "share" a DA DB ctx.
** In that case these counters are needed...
*/
pthread_mutex_t qsc_mutex;
uint qsc_maxthreads;
uint qsc_curactive;
#endif /* 0 */
uint32_t qsc_id_cnt;
};
union qsg_ctx_U
{
qsm_gen_T qsg_gen_ctx;
qss_ctx_T qsg_ss_ctx;
qsc_ctx_T qsg_sc_ctx;
};
#if 0
should this be instead like this:
struct qsg_ctx_S
{
sm_magic_T sm_magic;
rcbcom_ctx_T qsm_gen_com;
qmgr_ctx_P qsm_gen_qmgr_ctx;
int qsm_gen_id;
uint32_t qsm_gen_bit;
qss_status_T qsm_gen_status;
union qsg_parts_U
{
struct qss_part_S
{
#if MTA_USE_SMTPS_MUTEX
pthread_mutex_t qss_mutex;
#endif
uint qss_max_thrs;
uint qss_max_cur_thrs;
uint qss_cur_session;
#if QMGR_STATS
uint qss_max_session;
#endif
} qsg_qss_part;
struct qsc_part_S
{
uint8_t qsc_idx;
dadb_ctx_P qsc_dadb_ctx;
uint32_t qsc_id_cnt;
} qsg_qsc_part;
};
};
#endif /* 0 */
/* QMGR status */
#define QMGR_ST_NONE 0x00 /* not yet OK */
#define QMGR_ST_INIT0 0x01 /* initialized */
#define QMGR_ST_CONF 0x02 /* configured */
#define QMGR_ST_INIT1 0x04 /* initialized */
#define QMGR_ST_START 0x08 /* started */
#define QMGR_ST_OK 0x10 /* initialized, running normal */
#define QMGR_ST_SLOW 0x20 /* slow down */
#define QMGR_ST_RSR 0x40 /* resource problem, prepare to terminate */
#define QMGR_ST_SH_DOWN 0x80 /* shutting down */
#define QMGR_ST_STOPPED 0x81 /* stopped: almost terminated */
/* qmgr is about to terminate, don't start anything new */
#define qmgr_is_term(qmgr_ctx) ((qmgr_ctx)->qmgr_status >= QMGR_ST_SH_RSR)
/* qmgr is stopping, abort everything */
#define qmgr_is_stop(qmgr_ctx) ((qmgr_ctx)->qmgr_status >= QMGR_ST_SH_DOWN)
/* SMTPS status (in QMGR) */
#define QSS_ST_NONE 0x00 /* not yet OK */
#define QSS_ST_START 0x01 /* started */
#define QSS_ST_OK 0x10 /* initialized, running normal */
#define QSS_ST_SLOW_0 0x20 /* slow down */
#define QSS_ST_SLOW_1 0x21 /* slow down more */
#define QSS_ST_SLOW_2 0x22 /* slow down a lot */
#define QSS_ST_SLOW_3 0x23 /* slow down to stand still */
#define QSS_ST_SH_DOWN 0x80 /* shutting down */
#define QSS_ST_STOPPED 0x81 /* stopped: almost terminated */
#define QSS_ID_NONE (-1) /* no id (yet) */
/* for qX_control: direction */
#define QMGR_THROTTLE 1
#define QMGR_UN_THROTTLE (-1)
#define QMGR_ANY_THROTTLE 0
#define qmgr_is_throttle(dir) ((dir) >= 0)
#define qmgr_is_un_throttle(dir) ((dir) <= 0)
/* SMTPC status (in QMGR) */
#define QSC_ST_NONE 0x00 /* not yet OK */
#define QSC_ST_START 0x01 /* started */
#define QSC_ST_OK 0x10 /* initialized, running normal */
#define QSC_ST_SLOW_0 0x20 /* slow down */
#define QSC_ST_SLOW_1 0x21 /* slow down more */
#define QSC_ST_SLOW_2 0x22 /* slow down a lot */
#define QSC_ST_SLOW_3 0x23 /* slow down to stand still */
#define QSC_ST_SH_DOWN 0x80 /* shutting down */
#define QSC_ST_STOPPED 0x81 /* stopped: almost terminated */
#define QSC_IS_RUNNING(qsc_ctx) (((qsc_ctx)->qsc_status >= QSC_ST_OK) \
&& ((qsc_ctx)->qsc_status <= QSC_ST_SLOW_2))
#define QSC_ID_NONE (-1) /* no id (yet) */
/* return codes from QMGR modules that decode/handle requests */
#define QMGR_R_WAITQ SM_SUCCESS /* default: put back in waitq */
#define QMGR_R_ASYNC 1 /* do nothing, task has been put in waitq */
/*
** XXX define struct for iqdb; check sm-9 docs
** that struct also defines (more or less) the corresponding RCB types
session-id & session identifier
client-host & identification of connecting host
& IP address, host name, ident
features & features offered: AUTH, TLS, EXPN, ...
workarounds & work around bugs in client (?)
transaction-id & current transaction
reject-msg & message to use for rejections (needed?)
auth & AUTH information
starttls & TLS information
n-bad-cmds & number of bad SMTP commands
n-transactions & number of transactions
n-rcpts & total number of recipients
n-bad-rcpts & number of bad recipients
Transaction:
transaction-id & transaction identifier
start-time & date/time of transaction
mail & address, arguments (decoded?)
n-rcpts & number of recipients
rcpt-list & addresses, arguments (decoded?)
cdb-id & CDB identifier (obtained from cdb?)
msg-size & message size
n-bad-cmds & number of bad SMTP commands (necessary?)
n-rcpts & number of valid recipients
n-bad-rcpts & number of bad recipients
session-id & (pointer back to) session
end-time & end of transaction
** XXX define create/delete functions (which ctx to use?)
*/
/*
** QMGR SMTPS session context; only stored in iqdb.
*/
struct qss_sess_S
{
#if QS_SE_CHECK
sm_magic_T sm_magic;
#endif
sessta_id_T qsses_id; /* SMTPS session id */
time_T qsses_st_time; /* start time */
sm_rpool_P qsess_rpool;
in_addr_T qsess_client; /* XXX use a generic struct! */
#if 0
/*
** XXX we should have one data type for this...
** IPv4/IPv6 address ({client_addr})
** authinfo (?)
** resolved hostname ({client_name})
** info about resolve ({client_resolve})
** XXX other means to identify client host?
*/
sm_str_P qsses_clt_name;
#endif /* 0 */
/* uint8_t qsses_locked; not yet used */
};
/* we need only the external representation of addresses here */
struct qss_mail_S
{
sm_str_P qsm_pa; /* printable addr */
/* XXX parameters? */
};
struct qss_rcpt_S
{
sm_str_P qsr_pa; /* printable addr */
smtp_status_T qsr_status; /* status */
rcpt_idx_T qsr_idx; /* rcpt idx */
rcpt_id_T qsr_id; /* rcpt id */
uint qsr_flags; /* flags */
/* XXX parameters? */
TAILQ_ENTRY(qss_rcpt_S) qsr_link; /* links */
};
TAILQ_HEAD(qss_rcpts_S, qss_rcpt_S);
/* Operations on qss rcpt lists */
#define QSRCPTS_INIT(rcpts) TAILQ_INIT(rcpts)
#define QSRCPTS_EMPTY(rcpts) TAILQ_EMPTY(rcpts)
#define QSRCPTS_FIRST(rcpts) TAILQ_FIRST(rcpts)
#define QSRCPTS_END(rcpts) TAILQ_END(rcpts)
#define QSRCPTS_NEXT(rcpts) TAILQ_NEXT(rcpts, qsr_link)
#define QSRCPTS_PREV(rcpts) TAILQ_PREV(rcpts, qss_rcpts_S, qsr_link)
#define QSRCPTS_INSERT_TAIL(addr, rcpt) TAILQ_INSERT_TAIL(addr, rcpt, qsr_link)
#define QSRCPTS_INSERT_HEAD(addr, rcpt) TAILQ_INSERT_HEAD(addr, rcpt, qsr_link)
#define QSRCPTS_REMOVE(addr, rcpt) TAILQ_REMOVE(addr, rcpt, qsr_link)
#define QSRCPTS_REMOVE_FREE(ta, addr, rcpt) \
do \
{ \
TAILQ_REMOVE((addr), (rcpt), qsr_link); \
qsr_rcpt_free((ta), (rcpt)); \
} while (0)
/* qss recipient flags */
#define QSRCPT_FL_NONE 0x00000000
#define QSRCPT_FL_TA 0x00000001 /* added to qss_ta rcpts list */
#define QSRCPT_FL_IQDB 0x00000002 /* in iqdb */
#define QSRCPT_FL_IBDB 0x00000004 /* in ibdb */
#define QSRCPT_FL_T_SS 0x00000100 /* sent reply to SMTPS */
/* XXX more? How about QSRCPT_FL_IQDB_RM list for QSS_TA_FL_? */
#define QSRCPT_SET_FLAG(qsr, fl) (qsr)->qsr_flags |= (fl)
#define QSRCPT_CLR_FLAG(qsr, fl) (qsr)->qsr_flags &= ~(fl)
#define QSRCPT_IS_FLAG(qsr, fl) (((qsr)->qsr_flags & (fl)) != 0)
/* XXX other macros to manipulate the flag field in other structures? */
/*
** QMGR SMTPS transaction context
**
** qss_ta can be stored in iqdb, optas, aq (see below), and in ibdb.
** Hence it is not clear when qss_ta can be free()d. To avoid
** race conditions, flags are used which indicate what happened to
** qss_ta so far (i.e., in which places is it stored, where has it
** been removed, etc).
**
** About qss_ta in AQ: see qda_upd_iqdb(), currently it uses qss_ta to update
** IBDB; maybe this can be changed to use data from AQ?
*/
struct qss_ta_S
{
#if QS_TA_CHECK
sm_magic_T sm_magic;
#endif
sm_rpool_P qssta_rpool;
time_T qssta_st_time;
qss_mail_P qssta_mail; /* mail from */
qss_rcpts_T qssta_rcpts; /* rcpts */
uint qssta_rcpts_tot; /* total number of recipients */
uint qssta_flags;
sessta_id_T qssta_id;
cdb_id_P qssta_cdb_id;
off_t qssta_msg_sz_b;
qss_ctx_P qssta_ssctx; /* pointer back to SMTPS ctx */
sm_hdrmodhd_P qssta_hdrmodhd;
/*
** do we need a pointer to the sesssion?
** might be useful but non-trivial to achieve.
** the protocol would have to transfer the session id when a new
** transaction is created (NTAID).
** qss_sess_P qssta_sess; * pointer to sesssion
*/
#if 0
cmd-failures /* number of failures for certain commands */
#endif
pthread_mutex_t qssta_mutex;
/* uint8_t qssta_locked; not yet used */
};
NOTE(MUTEX_PROTECTS_DATA(qss_ta_S::qssta_mutex, qss_ta_S::))
/*
** qss_ta flags
*/
#define QSS_TA_FL_NONE 0x00000000
#define QSS_TA_FL_VERP 0x00000001
#define QSS_TA_FL_DB_MASK 0x0000001f /* mask for DB flags */
/* XXX See below: QSS_TA_OK_FREE() */
#define QSS_TA_FL_IQDB 0x00000010 /* in iqdb */
#define QSS_TA_FL_OPTA 0x00000020 /* in optas */
#define QSS_TA_FL_IBDB 0x00000040 /* in ibdb */
/* #define QSS_TA_FL_IBDB_C 0x00000080 * committed to ibdb */
#define QSS_TA_FL_AQ 0x00000100 /* referenced from AQ */
#define QSS_TA_FL_DB 0x000001f0 /* in some DB */
#define QSS_TA_FL_ADDED(flags) (((flags) & QSS_TA_FL_DB) >> 4)
#define QSS_TA_FL_IQDB_RM 0x00001000 /* removed from iqdb */
#define QSS_TA_FL_OPTA_RM 0x00002000 /* removed from optas */
#define QSS_TA_FL_IBDB_RM 0x00004000 /* "removed" from ibdb */
/* #define QSS_TA_FL_IBDB_C_RM 0x00008000 * committed "removed" from ibdb */
#define QSS_TA_FL_AQ_RM 0x00010000 /* no more ref. from AQ */
#define QSS_TA_FL_DB_RM 0x0001f000 /* removed from some DB */
#define QSS_TA_FL_RMED(flags) (((flags) & QSS_TA_FL_DB_RM) >> 12)
/*
** Two different flags: added to IBDB, committed to IBDB?
** It's hard to determine when an entry is actually committed;
** for open transactions this information is in optas, but for
** transactions that are closed, this information isn't anywhere (is it?).
** Therefore the "committed" flags are not (yet) used.
*/
/*
** OK to free iff QSS_TA_FL_xdb (A) == QSS_TA_FL_xdb_RM (R)
** That's not really correct: for every flag QSS_TA_FL_xdb the corresponding
** _RM flag must be set too. That's simply "implies": A=>B == !A || B.
*/
#define QSS_TA_OK_FREE(flags) \
(((~QSS_TA_FL_ADDED(flags) & QSS_TA_FL_DB_MASK) | \
QSS_TA_FL_RMED(flags)) == QSS_TA_FL_DB_MASK)
#if 0
(QSS_TA_FL_ADDED(flags) == QSS_TA_FL_RMED(flags))
(((~QSS_TA_FL_ADDED(flags) & 0x1f) || QSS_TA_FL_RMED(flags)) != 0)
#endif
/* more? */
#define QSS_TA_SET_FLAG(qss_ta, fl) (qss_ta)->qssta_flags |= (fl)
#define QSS_TA_CLR_FLAG(qss_ta, fl) (qss_ta)->qssta_flags &= ~(fl)
#define QSS_TA_IS_FLAG(qss_ta, fl) (((qss_ta)->qssta_flags & (fl)) != 0)
/*
** Flags for qss_ta_free()
** The first two aren't really used, they are useful for logging;
** only the last one is used: it overrides the requirement (see above)
** for free()ing qss_ta.
*/
#define QSS_TA_FREE_DA 0x00001 /* DA: remove qss_ta */
#define QSS_TA_FREE_OP 0x00002 /* OPtas: remove qss_ta */
#define QSS_TA_FREE_ALWAYS 0x00008 /* unconditionally free qss_ta */
/*
** Convert a status code from SMTPC/DA to an SMTP status code
** Note: this is a hack... it is not clear
** - who should do the conversion
** - whether pseudo SMTP reply codes should be used at all
** (see qmgr/sched.c for another example)
** - whether all error code should be treated as temporary
** or whether some are actually permanent.
** XXX Do a "real" conversion from error code to pseudo SMTP reply code.
*/
#define STATUS2SMTPCODE(st) ((sm_is_err(st)) ? SMTPC_TEMP_ST : (st))
/* type checks */
#if QS_TA_CHECK
# define SM_IS_QS_TA(qss_ta) SM_REQUIRE_ISA((qss_ta), SM_QSS_TA_MAGIC)
#else
# define SM_IS_QS_TA(qss_ta) SM_REQUIRE((qss_ta) != NULL)
#endif
#if QS_SE_CHECK
# define SM_IS_QS_SE(qss_sess) SM_REQUIRE_ISA((qss_sess), SM_QSS_SE_MAGIC)
#else
# define SM_IS_QS_SE(qss_sess) SM_REQUIRE((qss_sess) != NULL)
#endif
#define SM_IS_QS_RCPT(qss_rcpt) SM_REQUIRE((qss_rcpt) != NULL)
/*
** Use a list of transaction ids to commit?
** That list could contain just the entries in the iqdb
** which should contain the transactions to notify of the commit.
** We could even use a fixed size list and trigger a commit whenever
** some limit is reached (in addition to a short timeout).
** Currently implemented as fixed size queue, see <sm/fxszq.h>
** Alternatively add a list entry to qss_ta_S and make this structure
** just the head of the list (including counter and mutex).
*/
/* QMGR open transaction context (from SMTPS) */
struct qss_opta_S
{
uint qot_max; /* allocated size */
uint qot_cur; /* currently used (basically last-first) */
uint qot_first; /* first index to read */
uint qot_last; /* last index to read (first to write) */
pthread_mutex_t qot_mutex;
qss_ta_P *qot_tas; /* array of open transactions */
};
NOTE(MUTEX_PROTECTS_DATA(qss_opta_S::qot_mutex, qss_opta_S::))
#define SM_IS_QS_OPTAS(qss_optas) SM_REQUIRE((qss_optas) != NULL)
/*
** Task context QMGR/AR
*/
struct qar_ctx_S
{
sm_magic_T sm_magic;
rcbcom_ctx_T qar_com;
qmgr_ctx_P qar_qmgr_ctx; /* pointer back to main ctx */
/* split this in status and flags? */
qar_status_T qar_status; /* status of AR */
#if 0
uint32_t qar_last_id; /* last used id */
#endif
/* we'll abuse qar_wrmutex to protect access to this... */
};
/*
available threads (at least number)
tasks
waitqueue
runqueue
*/
/* DA (SMTPC) */
/* Return flags from various functions, must be less than 0x8000000 */
#define QDA_FL_ACT_SCHED 0x0001 /* activate scheduler */
#define QDA_FL_ACT_SMAR 0x0002 /* activate SMAR */
#define QDA_FL_ACT_DA 0x0004 /* activate DA */
#define QDA_ACT_SCHED(ret) (((ret) & QDA_FL_ACT_SCHED) != 0)
#define QDA_ACT_SMAR(ret) (((ret) & QDA_FL_ACT_SMAR) != 0)
#define QDA_ACT_DA(ret) (((ret) & QDA_FL_ACT_DA) != 0)
/* prototypes */
sm_ret_T qda_update_ta_stat(qmgr_ctx_P _qmgr_ctx
, sessta_id_T _da_ta_id
, sm_ret_T _status
, uint _err_st
, dadb_ctx_P dadb_ctx
, dadb_entry_P _dadb_entry
, aq_ta_P _aq_ta
, aq_rcpt_P _aq_rcpt
, sm_str_P errmsg
, thr_lock_T _locktype
);
/* SMTPC ids */
sm_ret_T qsc_id_init(qsc_ctx_P _scctx, uint32_t _id_val);
sm_ret_T qsc_id_end(qsc_ctx_P _scctx);
sm_ret_T qsc_id_next(qsc_ctx_P _qsc_ctx, uint _daprocidx, uint _dathreadidx, sessta_id_P _id);
sm_ret_T qsc_ctx_find(qmgr_ctx_P _qmgr_ctx, uint _sc_id, qsc_ctx_P *_pqsc_ctx);
/* AR */
sm_ret_T qmgr_rcpt2ar(qmgr_ctx_P _qmgr_ctx, aq_rcpt_P _aq_rcpt, thr_lock_T _locktype);
/* cleanup functions */
sm_ret_T qmgr_set_aq_cleanup(qcleanup_ctx_P _qcleanup_ctx, time_T _when, bool _changewakeup);
sm_ret_T qm_rcpt_da_expire(qmgr_ctx_P _qmgr_ctx, aq_rcpt_P _aq_rcpt, time_T _startt, time_T *_pexpire);
sm_ret_T qm_test_fill_aq(sm_log_ctx_P _lctx, aq_ctx_P _aq_ctx, time_T _time_now, ulong _tot_tas, uint32_t _once_tas, uint _max_fill_aq, uint _rate, sm_str_P _defaultdomain, uint32_t *_ptas_added, thr_lock_T _locktype);
#if QMGR_TEST
sm_ret_T qm_tst_fill_aq(qmgr_ctx_P _qmgr_ctx);
#endif
#endif /* SM_QMGR_INT_H */
syntax highlighted by Code2HTML, v. 0.9.1