/*
 * 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.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: qmgr_init.c,v 1.157 2007/10/23 04:01:41 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/qmgr.h"
#include "sm/evthr.h"
#include "sm/qmgr-int.h"
#include "sm/rdibdb.h"
#include "sm/fxszq.h"
#include "sm/hostname.h"
#include "sm/misc.h"
#include "sm/confsetpath.h"
#include "sm/rfc2821.h"
#include "qmgr.h"

/* XXX HACK XXX */
#define INIT_SEQ	1
sm_str_P NullSender;
#if QMGR_TEST
sm_cstr_P Tst_CDB_id;
#endif

/*
**  QMGR_INIT0 -- initialize QMGR, step 0: set default values
**	invoked before command line options are read
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**
**	Returns:
**		usual sm_error code
**
**	Last code review: 2003-10-17 04:21:09
*/

sm_ret_T
qmgr_init0(qmgr_ctx_P qmgr_ctx)
{
	uint u;
	sm_ret_T ret;
#if QMGR_TEST
	sessta_id_T ta_id;
#endif

	/* should we allocate the context here? */
	SM_REQUIRE(qmgr_ctx != NULL);

	/* clear out data */
	sm_memzero(qmgr_ctx, sizeof(*qmgr_ctx));

	/* initialize fds (0 could be valid hence they need to be set) */
	qmgr_ctx->qmgr_ss_li.qm_gli_lfd = INVALID_SOCKET;
	qmgr_ctx->qmgr_ss_li.qm_gli_nfd = 0;
	qmgr_ctx->qmgr_sc_li.qm_gli_lfd = INVALID_SOCKET;
	qmgr_ctx->qmgr_sc_li.qm_gli_nfd = 0;
	qmgr_ctx->qmgr_ar_fd = INVALID_SOCKET;
	qmgr_ctx->qmgr_ctllfd = INVALID_SOCKET;
	qmgr_ctx->qmgr_ctlfd = INVALID_SOCKET;
	qmgr_ctx->qmgr_cnf.q_cnf_iqdb_csize = IQDB_CSIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_iqdb_htsize = IQDB_HTSIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_occ_size = OCC_CSIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_aq_size = AQ_SIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_optas = IBDB_OPEN_TAS;
	qmgr_ctx->qmgr_cnf.q_cnf_ibdb_delay = IBDB_DELAY;
	qmgr_ctx->qmgr_cnf.q_cnf_ibdb_size = IBDB_SIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_min_delay = MIN_DELAY_NXT_TRY;
	qmgr_ctx->qmgr_cnf.q_cnf_max_delay = MAX_DELAY_NXT_TRY;
	qmgr_ctx->qmgr_cnf.q_cnf_tmo_return = QM_TMO_RETURN;
	qmgr_ctx->qmgr_cnf.q_cnf_tmo_delay = QM_TMO_DELAY;
	qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar = AQR_AR_TMOUT;
	qmgr_ctx->qmgr_cnf.q_cnf_tmo_da = AQR_DA_TMOUT;
	qmgr_ctx->qmgr_cnf.q_cnf_tmo_sched = AQR_SCHED_TMOUT;
	qmgr_ctx->qmgr_tmo_sched = AQR_SCHED_TMOUT;
	qmgr_ctx->qmgr_cnf.q_cnf_init_conc_conn = DA_INIT_CONC_CONN;
	qmgr_ctx->qmgr_cnf.q_cnf_max_conc_conn = DA_MAX_CONC_CONN;
	qmgr_ctx->qmgr_cnf.q_cnf_max_open_se = SS_MAX_OPEN_SE;
	qmgr_ctx->qmgr_cnf.q_cnf_max_conn_rate = SS_MAX_CONN_RATE;
	qmgr_ctx->qmgr_cnf.q_cnf_ss_cc_size = SS_CONNCTL_SIZE;
	qmgr_ctx->qmgr_cnf.q_cnf_lmtp_rcpts_ta = DA_MAX_RCPTS_TA;
	qmgr_ctx->qmgr_cnf.q_cnf_smtp_rcpts_ta = DA_MAX_RCPTS_TA;
	qmgr_ctx->qmgr_cnf.q_cnf_t_sched_dsn = QM_TMO_SCHED_DSN;
	qmgr_ctx->qmgr_cnf.q_cnf_dsn_rcpts_max = AQR_DSN_RCPTS_MAX;
	qmgr_ctx->qmgr_cnf.q_cnf_min_df = DISK_MIN_FREE;
	qmgr_ctx->qmgr_cnf.q_cnf_ok_df = DISK_OK_FREE;
	qmgr_ctx->qmgr_cnf.q_cnf_edb_cnf.edbcnf_chkpt_delay = EDB_SWEEP_DELAY;
	qmgr_ctx->qmgr_cnf.q_cnf_edb_cnf.edbcnf_oflags = EDB_OPEN_RECOVER
							|EDB_LOG_AUTOREMOVE;
	qmgr_ctx->qmgr_cnf.q_cnf_wait4srv = 1;
	qmgr_ctx->qmgr_cnf.q_cnf_wait4clt = 1;
	qmgr_ctx->qmgr_cnf.q_cnf_smarsock = smarsock;
	qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock = smsmtpcsock;
	qmgr_ctx->qmgr_cnf.q_cnf_smtpssock = smsmtpssock;
	qmgr_ctx->qmgr_cnf.q_cnf_cdb_base = "";
	qmgr_ctx->qmgr_cnf.q_cnf_ctlsock = NULL;
	qmgr_ctx->qmgr_cnf.q_cnf_min_thr = SM_QMGR_MIN_THR;
	qmgr_ctx->qmgr_cnf.q_cnf_max_thr = SM_QMGR_MAX_THR;
	qmgr_ctx->qmgr_cnf.q_cnf_max_fd = SM_QMGR_MAX_FD;
	qmgr_ctx->qmgr_cnf.q_cnf_loglevel = SM_LOG_LEVEL;

	qmgr_ctx->qmgr_status = QMGR_ST_INIT0;
	NullSender = sm_str_scpy(NULL, "<>", 4);

	ret = sm_log_create(NULL, &qmgr_ctx->qmgr_lctx, &qmgr_ctx->qmgr_lcfg);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init0, sm_log_create=%m\n", ret);
		goto error;
	}
	ret = sm_log_setfp_fd(qmgr_ctx->qmgr_lctx, smiolog, SMIOLOG_FILENO);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init0, sm_log_setfp=%m\n", ret);
		goto error;
	}
	ret = sm_log_setdebuglevel(qmgr_ctx->qmgr_lctx, qmgr_ctx->qmgr_cnf.q_cnf_loglevel);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init0, sm_log_setdebuglevel=%m\n",
			ret);
		goto error;
	}


#define SM_UPPER_CACHE_LIMIT	90
#define SM_LOWER_CACHE_LIMIT	70

	for (u = 0; u <= QMGR_RFL_LAST_I; u++) {
		qmgr_ctx->qmgr_lower[u] = SM_LOWER_CACHE_LIMIT;
		qmgr_ctx->qmgr_upper[u] = SM_UPPER_CACHE_LIMIT;
	}

#if 0
	/* set lower limit for disk space? */
	for (u = QMGR_RFL_CDB_I; u <= QMGR_RFL_LAST_I; u++)
		qmgr_ctx->qmgr_lower[u] = 0;
#endif /* 0 */

#if QMGR_TEST
	sm_snprintf(ta_id, SMTP_STID_SIZE, SMTPS_STID_FORMAT, (id_count_T)0, 0);
	Tst_CDB_id = sm_cstr_scpyn((const uchar *)ta_id, sizeof(ta_id));
	if (NULL == Tst_CDB_id)
		return sm_error_temp(SM_EM_Q, ENOMEM);
#endif

	/* this is a bit early ... */
	qmgr_ctx->sm_magic = SM_QMGR_CTX_MAGIC;
	qmgr_ctx->qmgr_cnf.sm_magic = SM_QMGR_CNF_MAGIC;
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  QMGR_INIT1 -- initialize QMGR
**	invoked after command line options are read
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**
**	Returns:
**		usual sm_error code
**
**	Last code review: 2003-10-17 04:37:37, see below
*/

sm_ret_T
qmgr_init1(qmgr_ctx_P qmgr_ctx)
{
	int i;
	sm_ret_T ret;
	size_t n;
	sm_evthr_ctx_P ev_ctx;
	qss_opta_P optas;
	sm_cstr_P mtype, mname;
	size_t confdirlen;
	const char *mpath;
	char confdir[PATH_MAX];

	SM_REQUIRE(qmgr_ctx != NULL);
	ev_ctx = NULL;

	/*
	**  Output error message?
	**  Complicated: the logging system isn't initialized yet
	**  (it's done somewhere in the middle). For now: use smioerr.
	*/

	if (NULL == qmgr_ctx->qmgr_hostname) {
		ret = sm_myhostname(&qmgr_ctx->qmgr_hostname);
		if (sm_is_err(ret)) {
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, sm_myhostname=%m\n"
				, ret);
			goto err0;
		}
	}
	if (NULL == qmgr_ctx->qmgr_pm_addr &&
	    NULL == qmgr_ctx->qmgr_cnf.q_cnf_pm_add)
	{
		n = sm_str_getlen(qmgr_ctx->qmgr_hostname) +
				strlen("<postmaster@>") + 2;
		qmgr_ctx->qmgr_pm_addr = sm_str_new(NULL, n, n + 1);
		if (NULL == qmgr_ctx->qmgr_pm_addr) {
			/* other error codes? */
			ret = sm_error_temp(SM_EM_Q, ENOMEM);
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, init=out_of_memory\n");
			goto err0;
		}
		if (sm_is_err(ret = sm_str_scopy(qmgr_ctx->qmgr_pm_addr,
					"<postmaster@")) ||
		    sm_is_err(ret = sm_str_cat(qmgr_ctx->qmgr_pm_addr,
					qmgr_ctx->qmgr_hostname)) ||
		    sm_is_err(ret = sm_str_scat(qmgr_ctx->qmgr_pm_addr, ">")))
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, scopy=%m\n", ret);
			goto err0;
		}
	}
	else if (NULL == qmgr_ctx->qmgr_pm_addr &&
	         NULL != qmgr_ctx->qmgr_cnf.q_cnf_pm_add)
	{
		sm_a2821_T addr;

		n = strlen(qmgr_ctx->qmgr_cnf.q_cnf_pm_add) + 1;
		qmgr_ctx->qmgr_pm_addr = sm_str_scpy(NULL,
				qmgr_ctx->qmgr_cnf.q_cnf_pm_add, n);
		if (NULL == qmgr_ctx->qmgr_pm_addr) {
			/* other error codes? */
			ret = sm_error_temp(SM_EM_Q, ENOMEM);
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, init=out_of_memory\n");
			goto err0;
		}
		A2821_INIT_RP(&addr, NULL);
		ret = t2821_scan((sm_rdstr_P) qmgr_ctx->qmgr_pm_addr, &addr, 0);
		if (!sm_is_err(ret))
			ret = t2821_parse(&addr, R2821_CORRECT|R2821_EMPTY);
		if (sm_is_err(ret)) {
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, address=\"%@s\", status=invalid, parse=%m\n",
				qmgr_ctx->qmgr_cnf.q_cnf_pm_add, ret);
			SM_STR_FREE(qmgr_ctx->qmgr_pm_addr);
			goto err0;
		}
		(void) a2821_free(&addr);
	}

	ret = sm_dirname(qmgr_ctx->qmgr_cnf.q_cnf_conffile, confdir,
			sizeof(confdir));
	if (sm_is_err(ret))
		goto err0;
	confdirlen = strlen(confdir);

	/* initialize control RCB only if socket is specified */
	if (qmgr_ctx->qmgr_cnf.q_cnf_ctlsock != NULL &&
	    *qmgr_ctx->qmgr_cnf.q_cnf_ctlsock != '\0')
	{
		ret = sm_rcbcom_open(&qmgr_ctx->qmgr_ctl_com);
		if (sm_is_err(ret))
			goto err0;
	}

	ret = sm_gen_conf_path(qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
		qmgr_ctx->qmgr_cnf.q_cnf_smtpssock,
		smsmtpssock,
		&qmgr_ctx->qmgr_cnf.q_cnf_smtpssock_abs,
		&qmgr_ctx->qmgr_cnf.q_cnf_smtpssock_alloc);
	if (sm_is_err(ret))
		return ret;

	ret = sm_gen_conf_path(qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
		qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock,
		smsmtpcsock,
		&qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock_abs,
		&qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock_alloc);
	if (sm_is_err(ret))
		return ret;

	ret = sm_gen_conf_path(qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
		qmgr_ctx->qmgr_cnf.q_cnf_smarsock,
		smarsock,
		&qmgr_ctx->qmgr_cnf.q_cnf_smarsock_abs,
		&qmgr_ctx->qmgr_cnf.q_cnf_smarsock_alloc);
	if (sm_is_err(ret))
		return ret;

	if (qmgr_ctx->qmgr_cnf.q_cnf_ctlsock != NULL &&
		*qmgr_ctx->qmgr_cnf.q_cnf_ctlsock != '\0') {
		ret = sm_gen_conf_path(qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			qmgr_ctx->qmgr_cnf.q_cnf_ctlsock,
			NULL,
			&qmgr_ctx->qmgr_cnf.q_cnf_ctlsock_abs,
			&qmgr_ctx->qmgr_cnf.q_cnf_ctlsock_alloc);
		if (sm_is_err(ret))
			return ret;
	}

	ret = cdb_start(qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			&qmgr_ctx->qmgr_cdb_ctx);
	if (sm_is_err(ret))
		return ret;

	qmgr_ctx->qmgr_iqdb = iqdb_open(qmgr_ctx->qmgr_cnf.q_cnf_iqdb_csize,
				qmgr_ctx->qmgr_cnf.q_cnf_iqdb_htsize,
				qm_iqdb_create, NULL, qmgr_ctx);
	if (NULL == qmgr_ctx->qmgr_iqdb) {
		/* other error codes? */
		ret = sm_error_temp(SM_EM_Q, ENOMEM);
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, iqdb_open=out_of_memory\n");
		goto err0;
	}

	/* initialize pthreads */
	ret = thr_init();
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, thr_init=%m\n", ret);
		goto err1;
	}

	i = pthread_mutex_init(&qmgr_ctx->qmgr_mutex, SM_PTHREAD_MUTEXATTR);
	if (i != 0) {
		ret = sm_error_perm(SM_EM_Q, i);
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, mutex_init=%m\n", i);
		goto err2;
	}

	/* initialize RCB list */
	RCBL_INIT(&qmgr_ctx->qmgr_rcbh);

	/* initialize event threads system */
	ret = evthr_init(&ev_ctx,
		qmgr_ctx->qmgr_cnf.q_cnf_min_thr,
		qmgr_ctx->qmgr_cnf.q_cnf_max_thr,
		qmgr_ctx->qmgr_cnf.q_cnf_max_fd);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, evthr_init=%m\n", ret);
		goto err3;
	}
#if EVTHR_DEBUG
	evthr_set_dbglvl(ev_ctx, qmgr_ctx->qmgr_evthr_dbg_lvl);
#endif

	/*
	**  HACK! it should check whether log is in config file
	**  however it's not clear how to do that...
	**  this only works because facility 0 is KERN.
	*/

	if (qmgr_ctx->qmgr_cnf.q_cnf_log.sm_logspc_facility != 0) {
		ret = sm_log_setfp_fd(ev_ctx->evthr_c_lctx, NULL, INVALID_FD);
		if (sm_is_err(ret))
			goto err5;
	}

	ret = sm_log_setdebuglevel(qmgr_ctx->qmgr_lctx, qmgr_ctx->qmgr_cnf.q_cnf_loglevel);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, sm_log_setdebuglevel=%m\n",
			ret);
		goto err5;
	}

	/* create SMTPS contexts */
	for (i = 0; i < QM_N_SS_GLI(qmgr_ctx); i++) {
		ret = qss_ctx_new(qmgr_ctx,
			(qss_ctx_P *) &(qmgr_ctx->qmgr_ss_li.qm_gli_ctx[i]));
		if (sm_is_err(ret)) {
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, qss_ctx_new=%m, i=%d\n",
				ret, i);
			goto err5;
		}
	}
	qmgr_ctx->qmgr_ss_li.qm_gli_fct = qmgr_smtps;
	qmgr_ctx->qmgr_ss_li.qm_gli_qmgr_ctx = qmgr_ctx;
	qmgr_ctx->qmgr_ss_li.sm_magic = SM_GLI_MAGIC;

	/* initialize open transactions */
	optas = (qss_opta_P) sm_malloc(sizeof(*optas));
	if (NULL == optas) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, optas_malloc=out_of_memory\n");
		goto err5;
	}
	FSQ_INIT(qmgr_ctx->qmgr_cnf.q_cnf_optas, optas->qot_max,
		optas->qot_cur, optas->qot_first, optas->qot_last);
	n = optas->qot_max * sizeof(*optas->qot_tas);
	optas->qot_tas = (qss_ta_P *) sm_zalloc(n);
	if (NULL == optas->qot_tas) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, optas_qot_tas_malloc=out_of_memory\n");
		goto err6;
	}
	i = pthread_mutex_init(&optas->qot_mutex, SM_PTHREAD_MUTEXATTR);
	if (i != 0) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, mutex_init=%i\n", i);
		ret = sm_error_perm(SM_EM_Q, i);
		goto err7;
	}

	/* create SMTPC contexts */
	for (i = 0; i < QM_N_SC_GLI(qmgr_ctx); i++) {
		ret = qsc_ctx_new(qmgr_ctx, (uint32_t) i,
			(qsc_ctx_P *) &(qmgr_ctx->qmgr_sc_li.qm_gli_ctx[i]));
		if (sm_is_err(ret)) {
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_init1, qsc_ctx_new=%m\n",
				ret);
			goto err8;
		}
	}
	qmgr_ctx->qmgr_sc_li.qm_gli_fct = qmgr_smtpc;
	qmgr_ctx->qmgr_sc_li.qm_gli_qmgr_ctx = qmgr_ctx;
	qmgr_ctx->qmgr_sc_li.sm_magic = SM_GLI_MAGIC;

	/* open active envelope db */
	ret = aq_open(qmgr_ctx, &qmgr_ctx->qmgr_aq,
			qmgr_ctx->qmgr_cnf.q_cnf_aq_size, 0);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, aq_open=%m\n", ret);
		goto err8;
	}
	null_cdb_id = sm_cstr_scpyn((const uchar *) "-", 1);
	if (NULL == null_cdb_id) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, cstr_scpyn=out_of_memory\n");
		goto err9;
	}

	ret = qar_ctx_new(qmgr_ctx, &qmgr_ctx->qmgr_ar_ctx);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, qar_ctx_new=%m\n", ret);
		goto err10;
	}

	/* Must be set before qm_rdibdb() is called */
	qmgr_ctx->qmgr_ev_ctx = ev_ctx;

/* max. number of directories in different FSs */
#define FS_DB_DIRS	32
	ret = fs_ctx_open(FS_DB_DIRS, &qmgr_ctx->qmgr_fs_ctx);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, fs_ctx_open=%m\n", ret);
		goto err11;
	}
	ret = cdb_fsctx_open(qmgr_ctx->qmgr_fs_ctx,
			(const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			&qmgr_ctx->qmgr_cdb_fsctx,
			&qmgr_ctx->qmgr_cdb_kbfree);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, cdb_fsctx_open=%m\n", ret);
		goto err12;
	}

	/* XXX Move this after edb_open()? */
	ret = edb_fsctx_open(qmgr_ctx->qmgr_fs_ctx,
			(const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			&qmgr_ctx->qmgr_edb_fsctx, &qmgr_ctx->qmgr_edb_kbfree);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, edb_fsctx_open=%m\n", ret);
		goto err13;
	}

	ret = edb_open(&qmgr_ctx->qmgr_cnf.q_cnf_edb_cnf,
			(const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			qmgr_ctx->qmgr_lctx, &qmgr_ctx->qmgr_edb);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, edb_open=%m\n", ret);
		goto error;
	}
	ret = edbc_open(&qmgr_ctx->qmgr_edbc, 0, 0);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, edbc_open=%m\n", ret);
		goto error;
	}

	/* must be done after DEFEDB, before IBDB  */
	ret = qm_rdibdb(qmgr_ctx);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, qm_rdibdb=%m\n", ret);
		goto error;
	}

	/* open ibdb, must be done after qm_rdibdb() */
	ret = ibdb_open((const char *)qmgr_ctx->qmgr_cnf.q_cnf_cdb_base,
			IBDB_NAME, SM_IO_WRONLY, INIT_SEQ,
			qmgr_ctx->qmgr_cnf.q_cnf_ibdb_size, IBDB_OFL_WRITE,
			qmgr_ctx->qmgr_fs_ctx,
			&qmgr_ctx->qmgr_ibdb);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, ibdb_open=%m\n", ret);
		goto error;
	}
	ret = ibdb_fs_getfree(qmgr_ctx->qmgr_ibdb,
			&qmgr_ctx->qmgr_ibdb_kbfree);
	if (sm_is_err(ret)) {
		sm_io_fprintf(smioerr,
			"sev=ERROR, func=qmgr_init1, ibdb_fs_getfree=%m\n", ret);
		goto error;
	}

	/* XXX write dummy record to IBDB to keep track of last id? */

	ret = sm_maps_init(&qmgr_ctx->qmgr_maps);
	if (NULL == qmgr_ctx->qmgr_maps) {
		ret = sm_error_temp(SM_EM_Q, ENOMEM);
		goto error;
	}
	ret = sm_bdb_class_create(qmgr_ctx->qmgr_maps);
	if (sm_is_err(ret))
		goto error;

#define QMGR_CONF_TYPE	"hash"
#define QMGR_CONF_NAME	"qmgr_conf"
#define QMGR_CONF_FILE	"qmgr_conf.db"

	mtype = sm_cstr_scpyn0((const uchar *)QMGR_CONF_TYPE,
			strlen(QMGR_CONF_TYPE));
	if (NULL == mtype) {
		ret = sm_error_temp(SM_EM_Q, ENOMEM);
		goto error;
	}
	mname = sm_cstr_scpyn0((const uchar *)QMGR_CONF_NAME,
			strlen(QMGR_CONF_NAME));
	if (NULL == mname) {
		ret = sm_error_temp(SM_EM_Q, ENOMEM);
		goto error;
	}
	qmgr_ctx->qmgr_conf_map = NULL;
	SM_GEN_MAP_PATH(mpath, qmgr_ctx->qmgr_cnf.q_cnf_confmap_path, confdir,
		qmgr_ctx->qmgr_cnf.q_cnf_confmap_fn, QMGR_CONF_FILE, error);
	ret = sm_map_open(qmgr_ctx->qmgr_maps, mname, mtype, 0,
			mpath, SMAP_MODE_RDONLY,
			&qmgr_ctx->qmgr_conf_map, SMPO_END);

	/* ignore errors for now, the map is optional... */
	if (sm_is_err(ret)) {
		QM_LEV_DPRINTFC(QDC_INIT, 3, (QM_DEBFP, "func=qmgr_init1, sm_map_open=%r, map=%p\n", ret, qmgr_ctx->qmgr_conf_map));
		qmgr_ctx->qmgr_conf_map = NULL;
#if 0
		goto error;
#endif /* 0 */
	}


	qmgr_ctx->qmgr_optas = optas;
	qmgr_ctx->qmgr_status = QMGR_ST_INIT1;
	return SM_SUCCESS;

	/*
	**  Error handling. Notice: to get rid of all these "goto errorXY"
	**	which complicates changing the order, all variables should
	**	be initialized to NULL (or another unique value which indicates
	**	that the _open()/_new() function hasn't been called yet). The
	**	_close()/_free() functions should allow NULL as value
	**	and simply return SM_SUCCESS. Then the functions can be
	**	called in no specific order except for a few like
	**	thread/mutex initialization.
	*/

  error:

	edbc_close(qmgr_ctx->qmgr_edbc);
	qmgr_ctx->qmgr_edbc = NULL;
	(void) edb_close(qmgr_ctx->qmgr_edb);
	qmgr_ctx->qmgr_edb = NULL;

	(void) edb_fsctx_close(qmgr_ctx->qmgr_edb_fsctx);

  err13:
	(void) cdb_fsctx_close(qmgr_ctx->qmgr_cdb_fsctx);

  err12:
	(void) fs_ctx_close(qmgr_ctx->qmgr_fs_ctx);
	qmgr_ctx->qmgr_fs_ctx = NULL;

  err11:
	(void) qar_ctx_free(qmgr_ctx->qmgr_ar_ctx);
	qmgr_ctx->qmgr_ar_ctx = NULL;
  err10:
	SM_CSTR_FREE(null_cdb_id);
  err9:
	aq_close(qmgr_ctx->qmgr_aq);
	qmgr_ctx->qmgr_aq = NULL;
  err8:
	/* qsc_ctx_free() can deal with NULL values */
	for (i = 0; i < QM_N_SC_GLI(qmgr_ctx); i++) {
		(void) qsc_ctx_free(qmgr_li_sc(qmgr_ctx, i));
		qmgr_li_sc_lv(qmgr_ctx, i) = NULL;
	}
  err7:
	SM_FREE(optas->qot_tas);
  err6:
	SM_FREE(optas);
  err5:
	/* qss_ctx_free() can deal with NULL values */
	for (i = 0; i < QM_N_SS_GLI(qmgr_ctx); i++) {
		(void) qss_ctx_free(qmgr_li_ss(qmgr_ctx, i));
		qmgr_li_ss_lv(qmgr_ctx, i) = NULL;
	}
	if (qmgr_ctx->qmgr_ibdb != NULL) {
		ibdb_close(qmgr_ctx->qmgr_ibdb);
		qmgr_ctx->qmgr_ibdb = NULL;
	}
	sm_log_destroy(qmgr_ctx->qmgr_lctx);
 /* err4: */
	if (ev_ctx != NULL) {
		(void) evthr_stop(ev_ctx);
		ev_ctx = NULL;
	}
  err3:
	(void) pthread_mutex_destroy(&qmgr_ctx->qmgr_mutex);
  err2:
	(void) thr_stop();
  err1:
	/* this should always be true, but better safe than sorry */
	if (qmgr_ctx->qmgr_iqdb != NULL) {
		iqdb_close(qmgr_ctx->qmgr_iqdb);
		qmgr_ctx->qmgr_iqdb = NULL;
	}
  err0:
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1