/*
 * 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: qmgr_rdcf.c,v 1.103 2007/07/13 04:17:02 ca Exp $")

#include "sm/common.h"
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/ctype.h"
#define QMGR_DEBUG_DEFINE 1
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "qmgr.h"
#include "sm/rfc2821.h"
#include "sm/sysexits.h"
#include "sm/version.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"
#include "sm/limits.h"
#include "sm/qmgrcnfdef.h"
#include "sm/util.h"

/*
**  QMGR_SHOWCOPT -- show compile time options
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**
**	Returns:
**		none
*/

static void
qmgr_showcopt(qmgr_ctx_P qmgr_ctx)
{
	sm_io_fprintf(smioout, "compile time options:\n"
#if QMGR_DEBUG
		"QMGR_DEBUG\n"
#endif
#if SM_DELAYED_DSN
		"SM_DELAYED_DSN\n"
#endif
#if QMGR_STATS
		"QMGR_STATS\n"
#endif
#if QMGR_TEST
		"QMGR_TEST\n"
#endif
#if QMGR_TEST_NO_AQUPDATE
	"QMGR_TEST_NO_AQUPDATE\n"
#endif
#if RCBCOMM_DEBUG
		"RCBCOMM_DEBUG\n"
#endif
		);
	(void) evthr_comptoptions(NULL, smioout);
}

/*
**  QMGR_SHOWVERSION -- show version information
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**		progname -- program name
**		cnf -- configuration file name
**		level -- how much detail?
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
qmgr_showversion(qmgr_ctx_P qmgr_ctx, const char *progname, const char *cnf, uint level)
{
	char *prefix;

	prefix = "";
	if (level >= SM_SV_RD_CONF)
		prefix = "# ";
	sm_io_fprintf(smioout, "%s%s: %s\n", prefix, progname, MTA_VERSION_STR);
	if (SM_SHOW_VERSION(level))
		sm_io_fprintf(smioout, "%sversion=0x%08x\n",
			prefix, MTA_VERSION);
	if (SM_SHOW_LIBS(level))
		(void) sm_bdbversionprt(smioout);
	if (SM_SHOW_OPTIONS(level))
		(void) qmgr_showcopt(qmgr_ctx);
	if (SM_SHOW_RD_CONF(level) && cnf != NULL && *cnf != '\0') {
		sm_io_fprintf(smioout, "# configuration-file=%s\n\n", cnf);
		(void) sm_conf_prt_conf(qmgr_global_defs, &qmgr_ctx->qmgr_cnf, smioout);
	}
	else if (SM_SHOW_DFLT_CONF(level)) {
		sm_io_fprintf(smioout, "# default configuration:\n\n");
		(void) sm_conf_prt_dflt(qmgr_global_defs, 0, smioout);
	}
	else if (SM_SHOW_ALL_CONF(level)) {
		sm_io_fprintf(smioout,
			"# configuration including flags.\n"
			"# note: this data cannot be used as configuration file\n"
			"# but it shows the available flags.\n"
			);
		(void) sm_conf_prt_dflt(qmgr_global_defs, SMC_FLD_FLAGS, smioout);
	}
	sm_io_flush(smioout);
	return sm_error_perm(SM_EM_Q_START, SM_E_DONTSTART);
}

/*
**  QMGR_USAGE -- Print QMGR usage message to smioerr
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**
**	Returns:
**		sm_error_perm(SM_EM_Q, SM_E_USAGE)
**
**	Last code review: 2003-10-17 16:10:54
*/

static sm_ret_T
qmgr_usage(qmgr_ctx_P qmgr_ctx)
{
	sm_io_fprintf(smioerr, "usage: qmgr [options]\n"
		"%s queue manager\n"
		"-A n        AQ size [%u] (must be at least 32)\n"
		"-B n        IBDB size (KB) [%u] (must be at least 32)\n"
		"-C n        Maximum number of concurrent connections to one IP addr [%d]\n"
		"-c n        Initial number of concurrent connections to one IP addr [%d]\n"
		"-D s        Initial delay for retries [%us]\n"
#if QMGR_DEBUG
		"-d n        Debug level\n"
#endif
#if EVTHR_DEBUG
		"-e n        Evthr debug level\n"
#endif
		"-F n        Configuration flags\n"
		"-f name     Name of configuration file [none]\n"
		"-I n        IQDB rsc size [%u] (must be at least 32)\n"
#if SM_HEAP_CHECK
		"-H n        Set heap check level\n"
#endif
		"-M s        Maximum delay for retries [%us] (must be greater than -D)\n"
		"-O H=n      IQDB hash table size (should be larger than rsc size) [%u]\n"
		"	     (must be at least 128)\n"
		"-O D=n      Maximum number of bounce messages in a DSN [%d]\n"

		"-O O=n      Maximum number of open connections (SMTPS) [%d]\n"
		"	     (must be at least 10)\n"
		"-O R=n      Maximum connection rate per 60s (SMTPS) [%d]\n"
		"-O T=n      Maximum number of open IBDB transactions [%d]\n"
		"-O m=n      Minimum free disk space (KB) [%d]\n"
		"-O o=n      ok free disk space (KB) [%d]\n"
		"-P address  Postmaster address for double bounces (must be in <>)\n"
		"-s n        Maximum time before scheduling a DSN [%us] (must be at least 1)\n"
		"-S a=name   Socket for communication with SMAR [%s]\n"
		"-S c=name   Socket for communication with SMTP clients [%s]\n"
		"-S C=name   Directory for CDB [%s]\n"
		"-S s=name   Socket for communication with SMTP servers [%s]\n"
#if QMGR_TEST
		"-t n        Set test flags\n"
#endif
		"-T n        Maximum time in queue [%us] (must be at least 2)\n"
		"-T a=n      Maximum time in AR [%us]\n"
		"-T d=n      Maximum time in DA [%us]\n"
		"-T D=n      Initial delay for retries [%us]\n"
		"-T i=n      Maximum time to acknowledge an IBDB transaction [%dmicro s]\n"
		"-T M=n      Maximum delay for retries [%us]\n"
		"-T w=n      send delay warning after this time in queue [%us] (>= 2)\n"
		"-T R=n      Maximum time in queue [%us] (must be at least 2)\n"
		"-v n        Set loglevel\n"
		"-V          Show version\n"
		"-w n        Time to wait for SMAR to be ready (must be at least 1)\n"
		"-W n        Time to wait for SMTPC to be ready (must be at least 1)\n"
#if QMGR_DEBUG
		"-x dbg.n    Set debug level to n for category dbg\n"
#endif
		, MTA_VERSION_STR
		, AQ_SIZE				/* -A */
		, IBDB_SIZE / 1024			/* -B */
		, DA_MAX_CONC_CONN			/* -C */
		, DA_INIT_CONC_CONN			/* -c */
		, MIN_DELAY_NXT_TRY			/* -D */
		, IQDB_CSIZE				/* -I */
		, MAX_DELAY_NXT_TRY			/* -M */
		, IQDB_HTSIZE				/* -O H */
		, AQR_DSN_RCPTS_MAX			/* -O D */
		, SS_MAX_CONN_RATE			/* -O O */
		, SS_MAX_OPEN_SE			/* -O R */
		, IBDB_OPEN_TAS				/* -O T */
		, DISK_MIN_FREE				/* -O m */
		, DISK_OK_FREE				/* -O o */
		, QM_TMO_SCHED_DSN			/* -s */
		, qmgr_ctx->qmgr_cnf.q_cnf_smarsock	/* -S a */
		, qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock	/* -S c */
		, qmgr_ctx->qmgr_cnf.q_cnf_cdb_base	/* -S C */
		, qmgr_ctx->qmgr_cnf.q_cnf_smtpssock	/* -S s */
		, QM_TMO_RETURN				/* -T */
		, AQR_AR_TMOUT				/* -T a */
		, AQR_DA_TMOUT				/* -T d */
		, MIN_DELAY_NXT_TRY			/* -T D */
		, IBDB_DELAY				/* -T i */
		, MAX_DELAY_NXT_TRY			/* -T M */
		, QM_TMO_DELAY				/* -T w */
		, QM_TMO_RETURN				/* -T R */
		);
	return sm_error_perm(SM_EM_Q, SM_E_USAGE);
}

/*
**  QMGR_RDCF -- read configuration
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**		argc -- number of arguments
**		argv -- vector of arguments
**
**	Returns:
**		usual sm_error code
**
**	Last code review: 2003-10-17 16:11:18
*/

sm_ret_T
qmgr_rdcf(qmgr_ctx_P qmgr_ctx, int argc, char *argv[])
{
	sm_ret_T ret, rv;
	int c, off;
	uint u, j, showversion;
	char *s;

	SM_IS_QMGR_CTX(qmgr_ctx);
	ret = SM_SUCCESS;
	showversion = 0;
	while ((c = getopt(argc, argv,
			"?A:B:C:c:D:d:e:F:f:H:hI:M:O:P:s:S:t:T:v:Vw:W:x:")) != -1)
	{
		switch (c)
		{
		  case 'A':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 32)
				qmgr_ctx->qmgr_cnf.q_cnf_aq_size = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'B':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 32)
				qmgr_ctx->qmgr_cnf.q_cnf_ibdb_size = u * 1024;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'C':
			qmgr_ctx->qmgr_cnf.q_cnf_max_conc_conn=
					(uint) strtoul(optarg, NULL, 0);
			break;

		  case 'c':
			qmgr_ctx->qmgr_cnf.q_cnf_init_conc_conn =
					(uint) strtoul(optarg, NULL, 0);
			break;

		  case 'D':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 0)
				qmgr_ctx->qmgr_cnf.q_cnf_min_delay = u * 2;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'd':
			j = (uint) strtoul(optarg, NULL, 0);
#if QMGR_DEBUG
			for (u = 0; u < SM_ARRAY_SIZE(qm_debug); u++)
				qm_debug[u] = j;
#endif
#if RCBCOMM_DEBUG
			rcbcomm_debug = j;
#endif
			break;

		  case 'e':
#if EVTHR_DEBUG
			qmgr_ctx->qmgr_evthr_dbg_lvl = (uint) strtoul(optarg, NULL, 0);
#endif
			break;

		  case 'F':
			u = (uint) strtoul(optarg, NULL, 0);
			qmgr_ctx->qmgr_cnf.q_cnf_flags = u;
			break;

		  case 'f':
			s = strdup(optarg);
			if (NULL == s) {
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=qmgr_rdcf, argument=\"%@s\", strdup=%m\n"
					, optarg, sm_err_temp(errno));
				ret = sm_error_perm(SM_EM_Q_START, EX_OSERR);
				break;
			}
			qmgr_ctx->qmgr_cnf.q_cnf_conffile = s;
			ret = qm_read_cnf(qmgr_ctx, s, &qmgr_ctx->qmgr_cnf.q_cnf_smc);
			if (sm_is_err(ret)) {
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=qmgr_rdcf, status=invalid_configuration_file, error=%m\n"
					, ret);
			}
			break;

		  case 'H':
#if 0
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 128)
				qmgr_ctx->qmgr_cnf.q_cnf_iqdb_htsize = u;
			else
				ret = qmgr_usage(qmgr_ctx);
#endif /* 0 */
#if SM_HEAP_CHECK
			SmHeapCheck = strtoul(optarg, NULL, 0);
#endif
			break;

		  case 'I':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 32)
				qmgr_ctx->qmgr_cnf.q_cnf_iqdb_csize = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'M':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 0 && u > qmgr_ctx->qmgr_cnf.q_cnf_min_delay)
				qmgr_ctx->qmgr_cnf.q_cnf_max_delay = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'O':
			if (optarg != NULL && ISALPHA(optarg[0])
			    && optarg[1] == '=')
				off = 2;
			else
				off = 0;

			u = (uint) strtoul(optarg + off, NULL, 0);
			if (off > 0) {
				switch (optarg[0])
				{
				  case 'H':
					if (u > 128)
						qmgr_ctx->qmgr_cnf.q_cnf_iqdb_htsize = u;
					else
						ret = qmgr_usage(qmgr_ctx);
					break;
				  case 'D':
					qmgr_ctx->qmgr_cnf.q_cnf_dsn_rcpts_max = u;
					break;
				  case 'm':
					if (u > 1000)
						qmgr_ctx->qmgr_cnf.q_cnf_min_df = u;
					else
						ret = qmgr_usage(qmgr_ctx);
					break;
				  case 'o':
					if (u > 1000 &&
					    u > qmgr_ctx->qmgr_cnf.q_cnf_min_df)
						qmgr_ctx->qmgr_cnf.q_cnf_ok_df = u;
					else
						ret = qmgr_usage(qmgr_ctx);
					break;
				  case 'R':
					qmgr_ctx->qmgr_cnf.q_cnf_max_conn_rate = u;
					break;
				  case 'T':
					qmgr_ctx->qmgr_cnf.q_cnf_optas = u;
					break;
				  case 'O':
				  default:
					qmgr_ctx->qmgr_cnf.q_cnf_max_open_se = u;
					break;
				}
			}
			else
				qmgr_ctx->qmgr_cnf.q_cnf_max_open_se = u;
			break;

		  case 'P':
			qmgr_ctx->qmgr_pm_addr = sm_str_scpy0(NULL, optarg, MAXADDRLEN);
			if (NULL == qmgr_ctx->qmgr_pm_addr) {
				ret = sm_error_temp(SM_EM_Q, ENOMEM);
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=qmgr_rdcf, status=cannot copy postmaster address, error=Cannot allocate memory\n");
				break;
			}
			else {
				sm_a2821_T addr;

				A2821_INIT_RP(&addr, NULL);
				rv = t2821_scan((sm_rdstr_P) qmgr_ctx->qmgr_pm_addr, &addr, 0);
				if (!sm_is_err(rv))
					rv = t2821_parse(&addr, R2821_CORRECT|R2821_EMPTY);
				if (sm_is_err(rv)) {
					sm_io_fprintf(smioerr,
						"sev=ERROR, func=qmgr_rdcf, address=\"%@s\", status=invalid, parse=%m\n",
						optarg, ret);
					SM_STR_FREE(qmgr_ctx->qmgr_pm_addr);
					ret = rv;
				}
				(void) a2821_free(&addr);
			}
			break;

		  case 's':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 1)
				qmgr_ctx->qmgr_cnf.q_cnf_t_sched_dsn = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'S':
			if (optarg != NULL && ISALPHA(optarg[0])
			    && optarg[1] == '=' && optarg[2] != '\0')
				off = 2;
			else {
				ret = qmgr_usage(qmgr_ctx);
				break;
			}
			s = NULL;
			switch (optarg[0])
			{
			  case 'a':
			  case 'c':
			  case 'C':
			  case 's':
				s = strdup(optarg + off);
				if (NULL == s) {
					sm_io_fprintf(smioerr,
						"sev=ERROR, func=qmgr_rdcf, argument=\"%@s\", strdup=%m\n"
						, optarg + off
						, sm_err_temp(errno));
					ret = sm_error_perm(SM_EM_Q_START, EX_OSERR);
				}
				break;
			  default:
				ret = qmgr_usage(qmgr_ctx);
				break;
			}
			switch (optarg[0])
			{
			  case 'a':
				qmgr_ctx->qmgr_cnf.q_cnf_smarsock = s;
				break;
			  case 'c':
				qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock = s;
				break;
			  case 'C':
				qmgr_ctx->qmgr_cnf.q_cnf_cdb_base = s;
				break;
			  case 's':
				qmgr_ctx->qmgr_cnf.q_cnf_smtpssock = s;
				break;
			  default:
				ret = qmgr_usage(qmgr_ctx);
				break;
			}
			s = NULL;
			break;

		  case 't':
#if QMGR_TEST
			u = (uint) strtoul(optarg, NULL, 0);
			qmgr_ctx->qmgr_cnf.q_cnf_tests = u;
#endif
			break;

		  case 'T':
			if (optarg != NULL && ISALPHA(optarg[0]) && optarg[1] == '=')
				off = 2;
			else
				off = 0;

			u = (uint) strtoul(optarg + off, NULL, 0);
			if (u > 1) {
				if (off > 0) {
					switch (optarg[0])
					{
					  case 'a':
						qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar = u;
						break;
					  case 'd':
						qmgr_ctx->qmgr_cnf.q_cnf_tmo_da = u;
						break;
					  case 'D':
						qmgr_ctx->qmgr_cnf.q_cnf_min_delay = u;
						break;
					  case 'i':
						if (u > 10)
							qmgr_ctx->qmgr_cnf.q_cnf_ibdb_delay = u;
						else
							ret = qmgr_usage(qmgr_ctx);
						break;
					  case 'M':
						qmgr_ctx->qmgr_cnf.q_cnf_max_delay = u;
						break;
					  case 'w':
						qmgr_ctx->qmgr_cnf.q_cnf_tmo_delay = u;
						break;
					  case 'R':
					  default:
						qmgr_ctx->qmgr_cnf.q_cnf_tmo_return = u;
						break;
					}
				}
				else
					qmgr_ctx->qmgr_cnf.q_cnf_tmo_return = u;
			}
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'v':
			qmgr_ctx->qmgr_cnf.q_cnf_loglevel =
					(uint) strtoul(optarg, NULL, 0);
			break;

		  case 'V':
			++showversion;
			break;

		  case 'w':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 0)
				qmgr_ctx->qmgr_cnf.q_cnf_wait4srv = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'W':
			u = (uint) strtoul(optarg, NULL, 0);
			if (u > 0)
				qmgr_ctx->qmgr_cnf.q_cnf_wait4clt = u;
			else
				ret = qmgr_usage(qmgr_ctx);
			break;

		  case 'x':
#if QMGR_DEBUG
			s = optarg;
			rv = sm_set_dbgcats(&s, qm_debug, SM_ARRAY_SIZE(qm_debug));
			if (sm_is_err(rv))
				ret = rv;
#endif

			break;


		  case 'h':
		  case '?':
		  default:
			ret = qmgr_usage(qmgr_ctx);
			break;
		}
	}
	if (showversion > 0) {
		ret = qmgr_showversion(qmgr_ctx, argv[0],
				qmgr_ctx->qmgr_cnf.q_cnf_conffile, showversion);
	}
	if (sm_is_success(ret)) {
		/* more plausibility checks? */
		if (qmgr_ctx->qmgr_cnf.q_cnf_min_df >= qmgr_ctx->qmgr_cnf.q_cnf_ok_df)
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=qmgr_rdcf, "
				"minimum_free_disk_space=%d, "
				"ok_free_disk_space=%d, "
				"status=misconfigured\n"
				, qmgr_ctx->qmgr_cnf.q_cnf_min_df
				, qmgr_ctx->qmgr_cnf.q_cnf_ok_df
				);
			ret = qmgr_usage(qmgr_ctx);
		}
		else
			qmgr_ctx->qmgr_status = QMGR_ST_CONF;
		if (qmgr_ctx->qmgr_cnf.q_cnf_conffile != NULL)
			qmgr_prt_cnf(&qmgr_ctx->qmgr_cnf, smioerr, true);
	}
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1