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