/* #ident "@(#)smail/src:RELEASE-3_2_0_121:smailconf.c,v 1.113 2005/07/25 05:12:46 woods Exp" */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * Copyright (C) 1992 Ronald S. Karr * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * smailconf.c: * loading and processing for the configuration files * * external functions: read_config_file, format_attribute, * print_config_variable, read_standard_file, * fill_attributes, find_config_attribute, * add_config_stat, is_newconf */ #include "defs.h" #include #include #include #include #include #include #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef __STDC__ # include #else # include #endif #if defined(HAVE_UNISTD_H) # include #endif #include #include "smail.h" #include "alloc.h" #include "list.h" #include "smailstring.h" #include "dys.h" #include "main.h" #include "parse.h" #include "addr.h" #include "log.h" #include "match.h" #include "direct.h" #include "route.h" #include "version.h" #include "transport.h" #include "smailconf.h" #include "exitcodes.h" #include "extern.h" #include "debug.h" #include "smailport.h" /* These 2 values are used in handling int values as suffix multipliers */ #define K_MULTIPLIER 1024 #define M_MULTIPLIER (1024*1024) /* functions local to this file */ static char *check_type __P((struct attr_table *, struct attribute *, char *, char *)); static char *convert_value __P((struct attr_table *, struct attribute *, u_attr_t *, smail_bool_t *, char *, char *, char *)); static struct attr_table *find_attribute __P((char *, struct attr_table *, struct attr_table *)); static const char *conf_var_typename __P((struct attr_table *)); static void print_config_help __P((void)); static void print_config_all __P((void)); /* variables local to this file */ /* * config_stat_list is used by add_config_stat() and is_newconf() to * determine if any configuration files have changed. */ struct config_stat *config_stat_list = NULL; /* * define the attributes which may be in the config file * * NOTE: keep these in sorted order, except always give duplicate/deprecated * names (i.e. those which point to the same variable) immediately after the * canonical name so that they will be skipped by print_config_all(); * (i.e. "smail -bP all"). * * IMPORTANT: Offset value must be "TRUE" for t_boolean attribues! */ static struct attr_table conf_attributes[] = { { "allow_one_mx_target_cname_hack", t_boolean, 0, FALSE, (tup_t *)&allow_one_mx_target_cname_hack, TRUE }, { "auth_domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&auth_domains, 0 }, /**/{ "auth_domain", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&auth_domains, 0 }, { "auto_mkdir", t_boolean, 0, FALSE, (tup_t *)&auto_mkdir, TRUE }, { "auto_mkdir_mode", t_mode, 0, 0, (tup_t *)&auto_mkdir_mode, 0 }, { "body_checks", t_string, FA_FMT_IS_PCRE_LIST, NULL, (tup_t *)&body_checks, 0 }, { "body_checks_always", t_string, FA_FMT_IS_PCRE_LIST, NULL, (tup_t *)&body_checks_always, 0 }, { "bypass_content_filtering_recipients", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&bypass_content_filtering_recipients, 0 }, { "config_file", t_string, 0, NULL, (tup_t *)&config_file, 0 }, { "console", t_string, 0, NULL, (tup_t *)&cons_fn, 0 }, { "copying_file", t_string, 0, NULL, (tup_t *)©ing_file, 0 }, #ifdef USE_CYRUS_IMAPD /* XXX is #ifdef'ing these a good idea? */ { "cyrus_deliver_path", t_string, 0, NULL, (tup_t *)&cyrus_deliver_path, 0 }, { "cyrus_group", t_string, 0, NULL, (tup_t *)&cyrus_group, 0 }, { "cyrus_noquota_passphrase", t_string, 0, NULL, (tup_t *)&cyrus_noquota_passphrase, 0 }, { "cyrus_user", t_string, 0, NULL, (tup_t *)&cyrus_user, 0 }, #endif { "daemon_pidfile", t_string, 0, NULL, (tup_t *)&daemon_pidfile, 0 }, { "date_field", t_string, 0, NULL, (tup_t *)&date_field, 0 }, { "delivery_grades", t_string, 0, NULL, (tup_t *)&delivery_grades, 0 }, { "delivery_mode", t_string, 0, NULL, (tup_t *)&delivery_mode_string, 0 }, { "director_file", t_string, 0, NULL, (tup_t *)&director_file, 0 }, { "domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&visible_domains, 0 }, /* XXX don't rename the variable to `domains'! */ /**/{ "domain", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&visible_domains, 0 }, /**/{ "visible_domains", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&visible_domains, 0 }, /**/{ "visible_domain", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&visible_domains, 0 }, { "error_copy_postmaster", t_boolean, 0, FALSE, (tup_t *)&error_copy_postmaster, TRUE }, { "flock_mailbox", t_boolean, 0, FALSE, (tup_t *)&flock_mailbox, TRUE }, { "fnlock_interval", t_int, 0, 0, (tup_t *)&fnlock_interval, 0 }, /* XXX t_interval */ { "fnlock_mode", t_mode, 0, 0, (tup_t *)&fnlock_mode, 0 }, { "fnlock_retries", t_int, 0, 0, (tup_t *)&fnlock_retries, 0 }, { "from_field", t_string, 0, NULL, (tup_t *)&from_field, 0 }, { "grades", t_string, 0, NULL, (tup_t *)&grades, 0 }, { "header_checks", t_string, FA_FMT_IS_PCRE_LIST, NULL, (tup_t *)&header_checks, 0 }, { "header_checks_always", t_string, FA_FMT_IS_PCRE_LIST, NULL, (tup_t *)&header_checks_always, 0 }, { "hit_table_len", t_int, 0, 0, (tup_t *)&hit_table_len, 0 }, { "host_lock_timeout", t_interval, 0, 0, (tup_t *)&host_lock_timeout, 0 }, { "hostnames", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&hostnames, 0 }, /**/{ "hostname", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&hostnames, 0 }, { "ignore_user_case", t_boolean, 0, FALSE, (tup_t *)&ignore_user_case, TRUE }, { "listen_name", t_string, 0, NULL, (tup_t *)&listen_name, 0 }, { "lock_by_name", t_boolean, 0, FALSE, (tup_t *)&lock_by_name, TRUE }, { "lock_mode", t_mode, 0, 0, (tup_t *)&lock_mode, 0 }, { "log_mode", t_mode, 0, 0, (tup_t *)&log_mode, 0 }, { "logfile", t_string, 0, NULL, (tup_t *)&log_fn, 0 }, { "max_hop_count", t_int, 0, 0, (tup_t *)&max_hop_count, 0 }, { "max_load_ave", t_double, 0.0, NULL, (tup_t *)&max_load_ave, 0 }, { "max_message_size", t_long, 0, 0L, (tup_t *)&max_message_size, 0 }, { "message_buf_size", t_int, 0, 0, (tup_t *)&message_buf_size, 0 }, { "message_id_field", t_string, 0, NULL, (tup_t *)&message_id_field, 0 }, { "message_log_mode", t_mode, 0, 0, (tup_t *)&message_log_mode, 0 }, { "method_dir", t_string, 0, NULL, (tup_t *)&method_dir, 0 }, { "more_hostnames", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&more_hostnames, 0 }, /**/{ "gateway_names", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&more_hostnames, 0 }, { "nobody", t_string, 0, NULL, (tup_t *)&nobody, 0 }, { "open_interval", t_int, 0, 0, (tup_t *)&open_interval, 0 }, /* XXX t_interval */ { "open_retries", t_int, 0, 0, (tup_t *)&open_retries, 0 }, { "paniclog", t_string, 0, NULL, (tup_t *)&panic_fn, 0 }, { "postmaster_address", t_string, 0, NULL, (tup_t *)&postmaster_address, 0 }, /**/{ "postmaster", t_string, FA_FMT_DEPRECATED, NULL, (tup_t *)&postmaster_address, 0 }, { "primary_name", t_string, 0, NULL, (tup_t *)&primary_name, 0 }, { "qualify_file", t_string, 0, NULL, (tup_t *)&qualify_file, 0 }, { "queue_only", t_boolean, 0, FALSE, (tup_t *)&queue_only, TRUE }, { "received_field", t_string, 0, NULL, (tup_t *)&received_field, 0 }, { "require_configs", t_boolean, 0, FALSE, (tup_t *)&require_configs, TRUE }, { "resolve_timeout", t_interval, 0, 0, (tup_t *)&resolve_timeout, 0 }, { "retry_duration", t_interval, 0, 0, (tup_t *)&retry_duration, 0 }, { "retry_file", t_string, 0, NULL, (tup_t *)&retry_file, 0 }, { "retry_interval", t_interval, 0, 0, (tup_t *)&retry_interval, 0 }, /**/{ "return_path_field", t_string, FA_FMT_DEPRECATED, NULL, (tup_t *)&return_path_field, 0 }, { "rfc1413_query_timeout", t_interval, 0, 0, (tup_t *)&rfc1413_query_timeout, 0 }, { "router_file", t_string, 0, NULL, (tup_t *)&router_file, 0 }, { "runq_grades", t_string, 0, NULL, (tup_t *)&runq_grades, 0 }, { "runq_max", t_int, 0, 0, (tup_t *)&runq_max, 0 }, { "second_config_file", t_string, 0, NULL, (tup_t *)&second_config_file, 0 }, { "sender_env_variable", t_string, 0, NULL, (tup_t *)&sender_env_variable, 0 }, { "sending_name", t_string, 0, NULL, (tup_t *)&sending_name, 0 }, { "smail", t_string, 0, NULL, (tup_t *)&smail, 0 }, { "smail_lib_dir", t_string, 0, NULL, (tup_t *)&smail_lib_dir, 0 }, { "smail_util_dir", t_string, 0, NULL, (tup_t *)&smail_util_dir, 0 }, { "smart_path", t_string, 0, NULL, (tup_t *)&smart_path, 0 }, { "smart_transport", t_string, 0, NULL, (tup_t *)&smart_transport, 0 }, { "smart_user", t_string, 0, NULL, (tup_t *)&smart_user, 0 }, { "smtp_accept_max", t_int, 0, 0, (tup_t *)&smtp_accept_max, 0 }, { "smtp_accept_queue", t_int, 0, 0, (tup_t *)&smtp_accept_queue, 0 }, { "smtp_allow_debug", t_boolean, 0, FALSE, (tup_t *)&smtp_allow_debug, TRUE }, /**/{ "smtp_debug", t_boolean, FA_FMT_DEPRECATED, FALSE, (tup_t *)&smtp_allow_debug, TRUE }, { "smtp_allow_expn", t_boolean, 0, FALSE, (tup_t *)&smtp_allow_expn, TRUE }, /**/{ "smtp_info", t_boolean, FA_FMT_DEPRECATED, FALSE, (tup_t *)&smtp_allow_expn, TRUE }, { "smtp_bad_mx_except", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_bad_mx_except, 0 }, { "smtp_bad_mx_targets", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_bad_mx_targets, 0 }, { "smtp_banner", t_string, 0, NULL, (tup_t *)&smtp_banner, 0 }, { "smtp_debug_pause", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_debug_pause, 0 }, { "smtp_error_delay", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_error_delay, 0 }, { "smtp_expn_delay", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_expn_delay, 0 }, { "smtp_greeting_delay", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_greeting_delay, 0 }, { "smtp_hello_broken_allow", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_hello_broken_allow, 0 }, { "smtp_hello_dnsbl_domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_hello_dnsbl_domains, 0 }, { "smtp_hello_dnsbl_except", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_hello_dnsbl_except, 0 }, { "smtp_hello_reject_broken_ptr", t_boolean, 0, FALSE, (tup_t *)&smtp_hello_reject_broken_ptr, TRUE }, { "smtp_hello_reject_dns_paranoid", t_boolean, FA_FMT_DEPRECATED, FALSE, (tup_t *)&smtp_hello_reject_broken_ptr, TRUE }, { "smtp_hello_reject_hostnames", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_hello_reject_hostnames, 0 }, { "smtp_hello_verify", t_boolean, 0, FALSE, (tup_t *)&smtp_hello_verify, TRUE }, { "smtp_hello_verify_literal", t_boolean, 0, FALSE, (tup_t *)&smtp_hello_verify_literal, TRUE }, { "smtp_hello_verify_ptr", t_boolean, 0, FALSE, (tup_t *)&smtp_hello_verify_ptr, TRUE }, { "smtp_host_dnsbl_domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_host_dnsbl_domains, 0 }, { "smtp_host_dnsbl_except", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_host_dnsbl_except, 0 }, { "smtp_host_reject_hostnames", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_host_reject_hostnames, 0 }, { "smtp_invalid_recipient_error_delay", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_invalid_recipient_error_delay, 0 }, { "smtp_local_sender_allow", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_local_sender_allow, 0 }, { "smtp_local_sender_restrict", t_boolean, 0, FALSE, (tup_t *)&smtp_local_sender_restrict, TRUE }, { "smtp_max_recipients", t_int, 0, 0, (tup_t *)&smtp_max_recipients, 0 }, { "smtp_max_bounce_recipients", t_int, 0, 0, (tup_t *)&smtp_max_bounce_recipients, 0 }, { "smtp_mua_only_hosts", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_mua_only_hosts, 0}, { "smtp_permit_mx_backup", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_permit_mx_backup, 0}, { "smtp_rbl_domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_rbl_domains, 0 }, { "smtp_rbl_except", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_rbl_except, 0 }, { "smtp_receive_command_timeout", t_interval, 0, 0, (tup_t *)&smtp_receive_command_timeout, 0 }, { "smtp_receive_message_timeout", t_interval, 0, 0, (tup_t *)&smtp_receive_message_timeout, 0 }, { "smtp_recipient_no_verify", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_recipient_no_verify, 0 }, { "smtp_reject_hosts", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_reject_hosts, 0 }, /**/{ "smtp_hello_reject_hosts", t_string, FA_FMT_IS_LIST | FA_FMT_DEPRECATED, NULL, (tup_t *)&smtp_reject_hosts, 0 }, /* was only in 3.2.0.112 - 3.2.0.114 */ { "smtp_remote_allow", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_remote_allow, 0 }, { "smtp_sender_no_verify", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_sender_no_verify, 0 }, { "smtp_sender_reject", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_sender_reject, 0 }, { "smtp_sender_reject_db", t_string, 0, NULL, (tup_t *)&smtp_sender_reject_db, 0 }, { "smtp_sender_reject_db_proto", t_string, 0, NULL, (tup_t *)&smtp_sender_reject_db_proto, 0 }, { "smtp_sender_reject_hostnames", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_sender_reject_hostnames, 0 }, { "smtp_sender_rhsbl_domains", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_sender_rhsbl_domains, 0 }, { "smtp_sender_rhsbl_except", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&smtp_sender_rhsbl_except, 0 }, { "smtp_sender_verify_mx_only", t_boolean, 0, FALSE, (tup_t *)&smtp_sender_verify_mx_only, TRUE }, { "smtp_vrfy_delay", t_int /* XXX t_interval */, 0, 0, (tup_t *)&smtp_vrfy_delay, 0 }, { "spool_dirs", t_string, FA_FMT_IS_LIST, NULL, (tup_t *)&spool_dirs, 0 }, { "spool_dir", t_string, 0, NULL, (tup_t *)&spool_dir, 0 }, /* set at runtime from spool_dirs */ { "spool_grade", t_char, 0, '\0', (tup_t *)&spool_grade, 0 }, { "spool_mode", t_mode, 0, 0, (tup_t *)&spool_mode, 0 }, { "switch_percent_and_bang", t_boolean, 0, FALSE, (tup_t *)&switch_percent_and_bang, TRUE }, { "transport_file", t_string, 0, NULL, (tup_t *)&transport_file, 0 }, { "trusted_users", t_string, 0, NULL, (tup_t *)&trusted_users, 0 }, /**/{ "trusted_user", t_string, FA_FMT_DEPRECATED, NULL, (tup_t *)&trusted_users, 0 }, /**/{ "trusted", t_string, FA_FMT_DEPRECATED, NULL, (tup_t *)&trusted_users, 0 }, { "trusted_groups", t_string, 0, NULL, (tup_t *)&trusted_groups, 0 }, /**/{ "trusted_group", t_string, FA_FMT_DEPRECATED, NULL, (tup_t *)&trusted_groups, 0 }, { "uucp_name", t_string, 0, NULL, (tup_t *)&uucp_name, 0 }, { "version", t_infoproc, 0, NULL, (tup_t *)version, 0 }, { "visible_name", t_string, 0, NULL, (tup_t *)&visible_name, 0 }, #ifdef FIXME { NULL, 0, 0, NULL, NULL, 0 } #endif }; /* FIXME: avoid this ugly ENDTABLE() stuff -- use null-entry termination semantics... */ /* point to the end of the configuration attributes table */ struct attr_table *end_conf_attributes = ENDTABLE(conf_attributes); /* * read_config_file - read a config file, or a colon separated list of config * files, and fill in conf_attributes * * return an error message or NULL (if no error). */ char * read_config_file(fn) char *fn; /* name of a config file */ { char *entry; /* entry found by read_entry */ FILE *f; /* input file */ struct stat statbuf; DEBUG1(DBG_CONF_MID, "read_config_file(): called for %s\n", fn); if (fn == NULL || EQ(fn, "-")) { /* a name of `-' turns off use of the external file */ return NULL; } /* * iterate through each file in succession if multiple names are given, * separated by colons, and recursively read and process them.... */ if (strchr(fn, ':')) { char *p_fn, *p_nextfn, *p_start, *p_end, *p_result; p_fn = COPY_STRING(fn); for (p_start = p_fn; (p_end = strchr(p_start, ':')); p_start = p_end + 1) { *p_end = '\0'; p_nextfn = make_lib_fn(p_start); p_result = read_config_file(p_nextfn); if (p_nextfn) { xfree(p_nextfn); } if (p_result) { if (p_fn) { xfree(p_fn); } return(p_result); /* pop any errors back out */ } } p_result = NULL; if (p_start && *p_start) { p_nextfn = make_lib_fn(p_start); p_result = read_config_file(p_nextfn); if (p_nextfn) { xfree(p_nextfn); } } if (p_fn) { xfree(p_fn); } return(p_result); } if (! (f = fopen(fn, "r"))) { DEBUG2(DBG_MAIN_MID, "read_config_file(): %s: fopen() failed: %s\n", fn, strerror(errno)); if (require_configs) { return xprintf("%s: %s", fn, strerror(errno)); } else if (errfile && debug > 1 && only_testing) { dprintf(errfile, "%s: %s: %s\n", program, fn, strerror(errno)); } add_config_stat(fn, (struct stat *)NULL); return NULL; } if (fstat(fileno(f), &statbuf) < 0) { DEBUG2(DBG_MAIN_LO, "read_config_file(): %s: fstat() failed: %s\n", fn, strerror(errno)); return xprintf("%s: %s", fn, strerror(errno)); } add_config_stat(fn, &statbuf); /* * read all of the entries in the config file */ while ((entry = read_entry(f, fn))) { char *error = NULL; struct attr_table *conf_attr; struct attribute *attr; smail_bool_t flags; #ifdef EXTREME_DEBUGGING DEBUG2(DBG_CONF_HI, "read_config_file(): read_entry(%v) returns:\n\t'%v'\n", fn, entry); #endif attr = parse_config(entry, &error); if (attr == NULL) { DEBUG1(DBG_CONF_LO, "read_config_file(): parse_config() failed: %s\n", error); return xprintf("%v: parse error: %s", fn, error); } conf_attr = find_config_attribute(attr->name); if (conf_attr == NULL) { DEBUG1(DBG_CONF_LO, "read_config_file(): find_config_attr() failed to find variable: %s\n", attr->name); return xprintf("%v: unknown variable: %v", fn, attr->name); } if (conf_attr->type == t_boolean) { flags = conf_attr->uptr->v_boolean; } conf_attr->value = attr->value; /* check if boolean value used was properly */ if ((error = check_type(conf_attr, attr, fn, "variable"))) { DEBUG2(DBG_CONF_LO, "read_config_file(): check_type(%s) failed for: %s\n", attr->name, error); return error; } /* check for deprecated names */ if (conf_attr->fmt_hints & FA_FMT_DEPRECATED) { if (getuid() == 0) { write_log(WRITE_LOG_TTY | WRITE_LOG_PANIC, "%s: %s: attribute name is deprecated -- see manual", fn, attr->name); } else if (errfile) { dprintf(errfile, "%s: %s: %s: attribute name is deprecated -- see manual\n", program, fn, attr->name); } } #if 0 DEBUG3(DBG_CONF_HI, "read_config_file(): %s variable '%v', about to get value:\n\t'%v'\n", conf_var_typename(conf_attr), conf_attr->name, conf_attr->value); #endif if ((error = convert_value(conf_attr, attr, conf_attr->uptr, &flags, (char *) NULL, fn, "variable"))) { DEBUG2(DBG_CONF_LO, "read_config_file(): convert_value(%s) failed for: %s\n", attr->name, error); return error; } if (conf_attr->type == t_boolean) { conf_attr->uptr->v_boolean = flags; #if 0 DEBUG4(DBG_CONF_HI, "read_config_file(): bool_t %v := %v, flags = 0x%lx, offset = 0x%lx\n", attr->name, attr->value, conf_attr->uptr->v_boolean, conf_attr->offset); #endif } } return NULL; } static char * check_type(conf_attr, attr, fn, attr_class) struct attr_table *conf_attr; struct attribute *attr; char *fn; /* configuration file attr was read from */ char *attr_class; /* "attribute" or "variable" */ { if (conf_attr->type == t_boolean) { /* make sure boolean types are given only boolean values */ if (!EQ(attr->value, on) && !EQ(attr->value, off)) { return xprintf("%v: boolean %s `%v' has non-boolean form of '%v'", fn, attr_class, attr->name, attr->value); } } else { /* make sure non-boolean types aren't given with '+'/'-' prefixes */ if ((attr->value == on && conf_attr->type != t_proc) || /* XXX does this ever make sense? */ (attr->value == off && conf_attr->type != t_string && conf_attr->type != t_char && conf_attr->type != t_int && conf_attr->type != t_mode && conf_attr->type != t_long && conf_attr->type != t_interval && conf_attr->type != t_double && conf_attr->type != t_proc && conf_attr->type != t_infoproc)) { return xprintf("%v: non-boolean %s `%v' has boolean form", fn, attr_class, attr->name); } } return (char *) NULL; } /* * convert_value(): fill in the attribute given the string representation from attr */ static char * convert_value(conf_attr, attr, uptr, flagsp, sp, fn, attr_class) struct attr_table *conf_attr; /* pointer to attribute entry to fill in */ struct attribute *attr; /* pointer to attribute parsed from config flie */ u_attr_t *uptr; /* pointer to storage for converted attribute */ smail_bool_t *flagsp; /* pointer to bit value for boolean attribute */ char *sp; /* structure pointer for t_proc calls */ char *fn; /* configuration file attr was read from */ char *attr_class; /* "attribute" or "variable" */ { char *errmsg = NULL; switch (conf_attr->type) { case t_string: /* special case, '-' prefix on string attr name sets string to NULL */ if (attr->value == off) { /* pointer equivalence */ uptr->v_string = NULL; } else { uptr->v_string = attr->value; } break; case t_boolean: if (!EQ(attr->value, on) && !EQ(attr->value, off)) { return xprintf("%v: bad boolean constant for %s: %v", fn, attr_class, attr->name); } #if 0 /* turn on the one below too for the final value and newline! */ DEBUG6(DBG_CONF_HI, "%v: convert_value(boolean %s): %v := %v, flags = 0x%lx, offset = 0x%lx", fn, attr_class, attr->name, attr->value, *flagsp, conf_attr->offset); #endif /* * NOTE: we can't use uptr->v_boolean because all boolean flags for * driver instances are stored as different bits in one "flags" * variable and we must toggle only the bit specified in the offset * value to control any given boolean attribute. */ if (EQ(attr->value, on)) { *flagsp |= conf_attr->offset; /* turn on bit given in offset */ } else { *flagsp &= ~conf_attr->offset; /* turn off bit given in offset */ } #if 0 DEBUG1(DBG_CONF_HI, ", final flags = 0x%lx\n", *flagsp); #endif break; case t_char: /* special case, '-' prefix on char attr name sets char to NUL */ if (attr->value == off) { /* pointer equivalence */ uptr->v_char = '\0'; } else if (strlen(attr->value) != 1) { return xprintf("%v: bad character constant for %s: %v", fn, attr_class, attr->name); } else { uptr->v_char = attr->value[0]; } break; case t_int: case t_mode: uptr->v_int = (int) c_atol(attr->value, &errmsg); if (errmsg) { return xprintf("%v: %s %v: %s", fn, attr_class, attr->name, errmsg); } break; case t_long: uptr->v_long = c_atol(attr->value, &errmsg); if (errmsg) { return xprintf("%v: %s %v: %s", fn, attr_class, attr->name, errmsg); } break; case t_interval: { long val; if ((val = ivaltol(attr->value)) < 0) { return xprintf("%v: %v %s: malformed interval value: %v", fn, attr_class, attr->name, attr->value); } uptr->v_interval = val; } break; case t_double: { double val; char c; /* catch sscanf spill over */ /* should be exactly one item set by sscanf */ if (sscanf(attr->value, "%lf%c", &val, &c) != 1) { return xprintf("%v: %v %s: malformed floating number: %v", fn, attr_class, attr->name, attr->value); } uptr->v_double = val; } break; case t_proc: if ((errmsg = (*((char * (*)__P((char *, struct attribute *))) conf_attr->uptr))(sp, attr))) { return xprintf("%v: %s", fn, errmsg); /* XXX errmsg leaks? */ } break; case t_infoproc: return xprintf("%v: %v: is a read-only %s!", fn, attr->name, attr_class); default: return xprintf("%v: %v: unknown %s type", fn, attr->name, attr_class); } return (char *) NULL; } /* * format_attribute - given a attribute, return a formatted value * * Parameters: * conf_attr - pointer to attribute entry * name - name of entry (for errors) * prefix - (returned) prefix, ie +/- for boolean * or # for infoproc types * value - (returned) value, maybe NULL * ptr - ptr to config struct * flags - bit values for all boolean flags * * Data may either be directly pointed to by the attribute struct, as in * global config variables, or may be an offset into a struct in which case * ptr must be set. * * Return value is bit mask of which items are valid */ int format_attribute(conf_attr, name, quoted, prefix, value, ptr, flags) struct attr_table * conf_attr; /* The attriute info */ char * name; /* The conf variable name */ int quoted; /* whether returned value should be quoted */ char * * prefix; /* Any prefix to be added to name */ char * * value; /* The value of the variable */ char * ptr; /* Pointer to anonymous struct */ smail_bool_t flags; /* flags entry from config */ { static struct str val; static int initialised = FALSE; tup_t * vptr; /* pointer to attr value */ int ret; /* return mask */ long lval; /* int/long conversion var */ /* * N.B.: This routine depends on the minimum length of val (which should * be STR_BUMP) being reasonable -- i.e. greater than 20, and not being * shrunk, because it uses sprintf() to insert certain types of values * directly into the allocated storage for val. If someone tampers behind * the scenes expect the whole lot to fall down :-( */ if (!initialised) { STR_INIT(&val); initialised++; } else { STR_CHECK(&val); STR_CLEAR(&val); } /* Initialise default values */ *prefix = NULL; *value = NULL; ret = 0; if (conf_attr == NULL) { if (getuid() == 0) { write_log(WRITE_LOG_TTY | WRITE_LOG_PANIC, "%s: unknown attribute: %v\n", program, name); } else if (errfile) { dprintf(errfile, "%s: unknown attribute: %v\n", program, name); } return ret; } #if 0 assert(EQ(name, conf_attr->name)); #endif if (ptr == NULL) { vptr = conf_attr->uptr; } else { vptr = (tup_t *) ((char *) ptr + conf_attr->offset); } switch (conf_attr->type) { case t_string: if (vptr->v_string) { #ifdef USE_CYRUS_IMAPD /* * XXX This is a very ugly hack. * * What we need instead is some generic attribute privacy flag.... * * Note that full debugging in delivery mode will reveal the secret. */ if ((operation_mode == PRINT_VARS_MODE || (debug > 1 && (operation_mode == TEST_MODE || operation_mode == VERIFY_ADDRS))) && EQ(conf_attr->name, "cyrus_noquota_passphrase")) { gid_t cyrus_gid = 0; /* assume wheel group members are privileged enough */ struct group *cygr = getgrnam(cyrus_group); if (cygr) { cyrus_gid = cygr->gr_gid; } else { DEBUG2(DBG_CONF_LO, "format_attribute(): cyrus_group of %v is invalid: %s\n", cyrus_group, strerror(errno)); } if (real_uid != 0 && real_gid != cyrus_gid) { vptr->v_string = "This secret is not for you to know!"; } else { DEBUG3(DBG_CONF_MID, "format_attribute(): reporting passphrase to uid=%d, gid=%d, for cyrus_gid=%d\n", real_uid, real_gid, cyrus_gid); } } #endif if (quoted) { if (conf_attr->fmt_hints & FA_FMT_IS_PCRE_LIST) { voidplist_t *tlist; char *error; error = NULL; tlist = compile_pcre_list(vptr->v_string, &error); if (!tlist) { panic(EX_DATAERR, "error re-compiling %v: %s\n", name, error); } *value = format_pcre_list(tlist); free_pcre_list(tlist); } else { *value = quote(vptr->v_string, FALSE, (conf_attr->fmt_hints & FA_FMT_IS_LIST) ? TRUE : FALSE); } } else { *value = vptr->v_string; } ret = FA_USE_VALUE; } else { *prefix = "-"; ret = FA_USE_PREFIX; } break; case t_boolean: { unsigned long int v2; /* Booleans are handled a bit differently in the drivers */ if (conf_attr->offset != 0) { /* If offset is non-zero, then use it as a mask on flags */ v2 = flags & conf_attr->offset; } else { v2 = vptr->v_boolean; } if (v2) { *value = "true"; *prefix = "+"; } else { *value = "false"; *prefix = "-"; } ret = FA_USE_PREFIX; break; } case t_char: if (vptr->v_char) { STR_NEXT(&val, vptr->v_char); STR_NEXT(&val, '\0'); *value = STR(&val); ret = FA_USE_VALUE; } else { *value = "\0"; *prefix = "-"; ret = FA_USE_PREFIX; } break; case t_int: case t_long: if (conf_attr->type == t_int) { lval = vptr->v_int; } else { lval = vptr->v_long; } if (lval == 0) { *value = "0"; *prefix = "-"; ret = FA_USE_PREFIX; } else { if ((lval >= M_MULTIPLIER) && !(lval % M_MULTIPLIER)) { str_printf(&val, "%ldM", (lval / M_MULTIPLIER)); } else if ((lval >= K_MULTIPLIER) && !(lval % K_MULTIPLIER)) { str_printf(&val, "%ldK", (lval / K_MULTIPLIER)); } else { str_printf(&val, "%ld", lval); } *value = STR(&val); ret = FA_USE_VALUE; } break; case t_mode: if (vptr->v_int == 0) { *value = "0"; *prefix = "-"; ret = FA_USE_PREFIX; } else { str_printf(&val, "0%o", vptr->v_int); *value = STR(&val); ret = FA_USE_VALUE; } break; case t_interval: if (vptr->v_interval == 0) { *value = "0"; *prefix = "-"; ret = FA_USE_PREFIX; } else { if ((*value = ltoival(vptr->v_interval)) == NULL) { str_printf(&val, "%ld", vptr->v_interval); *value = STR(&val); } ret = FA_USE_VALUE; } break; case t_double: (void) sprintf(STR(&val), "%g", vptr->v_double); *value = STR(&val); ret = FA_USE_VALUE; break; case t_proc: if (errfile) { dprintf(errfile, "%s: %v: cannot print proc attributes\n", program, name); } break; case t_infoproc: *value = quote((*((char *(*) __P((char *))) vptr))(name), FALSE, FALSE); *prefix = "#"; ret = FA_USE_VALUE | FA_USE_PREFIX | FA_IS_COMMENT; break; default: if (errfile) { panic(EX_SOFTWARE, "variable %v: unknown type\n", name); } break; } return(ret); } /* * print_config_variable - write the value for a config variable on stdout */ void print_config_variable(name) char *name; { struct attr_table *conf_attr; char *prefix; char *value; int ret; if (EQ(name, "help")) { print_config_help(); return; } if (EQ(name, "all") || EQ(name, "CONFIG")) { print_config_all(); return; } if (EQ(name, "DIRECTORS")) { dump_director_config(stdout); return; } if (EQ(name, "ROUTERS")) { dump_router_config(stdout); return; } if (EQ(name, "TRANSPORTS")) { dump_transport_config(stdout); return; } if (EQ(name, "QUALIFY")) { dump_qualify_config(stdout); return; } if (EQ(name, "RETRY")) { dump_retry_config(stdout); return; } if (EQ(name, "ALL")) { fputs("#\n# -- config file\n#\n", stdout); print_config_all(); fputs("#\n# -- end of config\n#\n", stdout); dump_director_config(stdout); dump_router_config(stdout); dump_transport_config(stdout); dump_qualify_config(stdout); dump_retry_config(stdout); return; } if (*name != '$') { conf_attr = find_config_attribute(name); if (conf_attr == NULL) { dprintf(stdout, "%s: unknown configuration attribute: %v\n", program, name); } else { smail_bool_t flags = 0; if (conf_attr->type == t_boolean) { flags = conf_attr->uptr->v_boolean; } ret = format_attribute(conf_attr, name, ((debug > 0) ? TRUE : FALSE), &prefix, &value, (char *) NULL, flags); if (ret) { if (debug) { dprintf(stdout, "%s%v%s%s\n", ((ret & FA_USE_PREFIX) ? prefix : ""), name, ((ret & FA_USE_VALUE) ? " = " : ""), ((ret & FA_USE_VALUE) ? value : "")); /* value is already quoted */ } else { (void) printf("%s\n", value ? ((*value) ? value : "# (empty-value)") : "# (null-value)"); } } else { (void) printf("print_config_variable(): format_attribute(): impossible result!\n"); } } } else { value = expand_string(name, (struct addr *) NULL, (char *) NULL, (char *) NULL); if (value) { if (debug >= DBG_CONF_LO) { /* assume everything's a list in debug mode */ dprintf(stdout, "%v = %s\n", name, quote(value, FALSE, (debug) ? TRUE : FALSE)); } else { dprintf(stdout, "\"%v\"\n", value); } xfree(value); } } return; } static const char * conf_var_typename(attr) struct attr_table *attr; { switch (attr->type) { case t_string: return "string"; case t_boolean: return "boolean"; case t_char: return "char"; case t_int: return "int"; case t_mode: return "mode"; case t_long: return "long"; case t_interval:return "interval"; case t_double: return "double"; case t_proc: return "special"; case t_infoproc:return "info"; default: return "unkown type"; } } /* * print_config_help - output a listing of all config attributes, with type */ static void print_config_help() { register struct attr_table *t; register tup_t *last_uptr = NULL; /* FIXME: avoid this ugly end_conf_attributes stuff -- use null-entry termination semantics... */ for (t = conf_attributes; t < end_conf_attributes; t++) { if (t->uptr == last_uptr) { continue; } last_uptr = t->uptr; (void) printf("%s = %s\n", t->name, conf_var_typename(t)); } } /* * print_config_all - print all config file variables */ static void print_config_all() { register struct attr_table *t; register tup_t *last_uptr = NULL; int save_debug = debug; debug = 1; /* FIXME: avoid this ugly end_conf_attributes stuff -- use null-entry termination semantics... */ for (t = conf_attributes; t < end_conf_attributes; t++) { if (t->uptr == last_uptr) { continue; /* skip duplicate names */ } if (t->fmt_hints & FA_FMT_DEPRECATED) { continue; /* skip deprecated names */ } last_uptr = t->uptr; switch(t->type) { case t_string: case t_boolean: case t_char: case t_int: case t_mode: case t_long: case t_interval: case t_double: case t_infoproc: /* XXX should have t_proc too? */ print_config_variable(t->name); break; default: (void) printf("%s: has unknown attribute type\n", t->name); break; } } debug = save_debug; } /* * read_standard_file - read (for example) a transport, director or router file * * given a template default structure, some information about that structure, * and an attr_table describing attributes, read a file of standard * attribute descriptions and return a list describing all of the entries * in that file. * * inputs: * f - open input file. File format must conform to what is * expected by read_entry and parse_entry in parse.c. * template - pointer to the template structure. * struct_size - size of template structure. * name_offset - offset to the name element in the template structure. * flags_offset - offset to flags element of structure. * succ_offset - offset to succ element of structure. * attr_table - table defining generic attributes. * end_attr_table- end of the attribute table * * driv_function - function to handle driver attributes for each entry. * Also, this function can check the validity of the * generic attributes in the passed structure. The * function should return NULL, or an error message. * It is called as: * * char * * f(s, d) * char *s; --- structure being defined * struct attribute *d; --- driver attributes * * If driv_function is NULL, no driver attributes are * allowed and no function is called. * head - pointer to a variable in which to store the head * of the generated structure list. * * output: * If an error occured, an error message will be returned. * Otherwise NULL will be returned and a pointer to the new * structure list will be stored at the location pointed to by * `head'. */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ char * read_standard_file(f, infn, template, struct_size, name_offset, flags_offset, succ_offset, attr_table, end_attr_table, driv_function, head) FILE *f; /* input file */ char *infn; /* input file name */ register char *template; /* template structure */ size_t struct_size; /* size of template structure */ int name_offset; /* name field offset in structure */ int flags_offset; /* where to store boolean flags */ int succ_offset; /* where to store succ pointer */ struct attr_table *attr_table; /* start of attribute table */ struct attr_table *end_attr_table; /* e.g., ENDTABLE(attr_table) */ char *(*driv_function) __P((char *, struct attribute *)); /* function to handle driver attrs */ char **head; /* output structure list */ { char **link_ptr; /* pointer to last succ element */ char *cur; /* current structure to define */ char *s; /* text for current entry */ DEBUG(DBG_CONF_MID, "read_standard_file called\n"); /* start out with no structure list entries */ link_ptr = head; *link_ptr = NULL; /* * scan through reading entries. For each entry allocate a * structure and fill it in. Also, link the new structure to the * previous structure. */ while ((s = read_entry(f, infn))) { char *error; struct attribute *generic_attrs; /* generic attributes for entry */ struct attribute *driver_attrs; /* driver attributes for entry */ char *name; /* name of entry */ /* create the new structure and forward link to it */ *link_ptr = cur = xmalloc(struct_size); link_ptr = (char **)(cur + succ_offset); /* fill in the entry structure, starting with the template */ (void) memcpy(cur, template, (size_t) struct_size); /* get all of the attributes */ name = parse_entry(s, &generic_attrs, &driver_attrs, &error); if (name == NULL) { #if 0 assert(error); #endif return error; } /* fill in the entry name */ *(char **)(cur + name_offset) = name; /* * fill in the generic attributes, calling attr_function for * attributes not in attr_table */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ error = fill_attributes(cur, generic_attrs, (smail_bool_t *) (cur + flags_offset), attr_table, end_attr_table, name); if (error) { return xprintf("in entry %s", error); /* name is already included in error */ } if (driv_function) { error = (*driv_function)(cur, driver_attrs); if (error) { return xprintf("in entry %v: %s", name, error); } } else if (driver_attrs) { return xprintf("in entry %v: no driver attributes allowed in file", name); } } /* everything went okay */ return NULL; } /* * dump_standard_config - print out the config used * * The reverse of read_standard_file - this prints out something * like the original config file! */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ void dump_standard_config(f, config, name, flags, attr_table, end_attr_table) FILE *f; /* output file */ register char *config; /* filled in config */ char * name; /* name of driver */ smail_bool_t flags; /* boolean flags */ struct attr_table *attr_table; /* start of attribute table */ struct attr_table *end_attr_table; /* e.g., ENDTABLE(attr_table) */ { int ret; /* return mask from format_attribue */ struct attr_table *attr; /* attribute entry */ char * prefix; /* returned prefix */ char * value; /* returned value */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ for (attr = attr_table; (attr < end_attr_table); attr++) { if (attr->fmt_hints & FA_FMT_DEPRECATED) { continue; /* skip deprecated items */ } if (attr->type != t_proc) { ret = format_attribute(attr, name, 1, &prefix, &value, config, flags); if (ret) { fprintf(f, "%s%s%s%s%s%s,\n", ((ret & FA_IS_COMMENT) ? "" : "\t"), ((ret & FA_USE_PREFIX) ? prefix : ""), ((ret & FA_IS_COMMENT) ? "\t" : ""), attr->name, ((ret & FA_USE_VALUE) ? "=" : ""), ((ret & FA_USE_VALUE) ? value : "")); } } } } /* * fill_attributes - build a structure and fill in attributes * * Given a list of attributes extracted from one of the configuration * files and an array of known attributes, fill in a stucture to which * those attributes pertain. * * inputs: * sp - pointer to stucture that is to be filled in * attrs - list of given attributes * flags - pointer to long where boolean flags should be OR'd in * attr_table - table describing known attributes * end_attr_table - end of known attribies (i.e., ENDTABLE(attr_table)) * name - combined filename and entry name where attrs were read from * * output: * returns either an error message, or NULL if everything went okay. */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ char * fill_attributes(sp, attrs, flagsp, attr_table, end_attr_table, name) char *sp; struct attribute *attrs; smail_bool_t *flagsp; struct attr_table *attr_table; struct attr_table *end_attr_table; char *name; { struct attribute *attr; char *error; /* step through all of the attributes from the file entry */ for (attr = attrs; attr; attr = attr->succ) { register struct attr_table *conf_attr; /* table entry */ /* find the attribute in the list of known attributes */ /* FIXME: avoid this ugly end_attr_table stuff -- use null-entry termination semantics... */ conf_attr = find_attribute(attr->name, attr_table, end_attr_table); if (conf_attr == NULL) { /* didn't find the attribute */ return xprintf("%s: unknown attribute", attr->name); } /* check if boolean value used was properly */ if ((error = check_type(conf_attr, attr, name, "attribute"))) { return error; } /* check for deprecated names */ if (conf_attr->fmt_hints & FA_FMT_DEPRECATED) { if (getuid() == 0) { write_log(WRITE_LOG_TTY | WRITE_LOG_PANIC, "%s: attribute name (in %v) is deprecated -- see manual", attr->name, name); } else if (errfile) { dprintf(errfile, "%s: %s: attribute name (in %v) is deprecated -- see manual\n", program, attr->name, name); } } /* fill in the attribute given the string representation from attr */ if ((error = convert_value(conf_attr, attr, (u_attr_t *) (sp + conf_attr->offset), flagsp, sp, name, "attribute"))) { return error; } } return NULL; } /* * find_attribute - find an attribute within an attribute table */ /* FIXME: avoid this ugly "end" stuff -- use null-entry termination semantics... */ static struct attr_table * find_attribute(name, table, end) register char *name; /* name to search for */ register struct attr_table *table; /* table to search in */ register struct attr_table *end; /* end of table */ { /* FIXME: avoid this ugly stuff -- use null-entry termination semantics... */ while (table < end) { if (EQ(name, table->name)) { return table; } table++; } return NULL; } struct attr_table * find_config_attribute(name) char *name; { /* FIXME: avoid this ugly end_conf_attributes stuff -- use null-entry termination semantics... */ return find_attribute(name, conf_attributes, end_conf_attributes); } /* * add_config_stat - add a new config file to config_stat_list * * Pass a filename plus a pointer to a stat structure or NULL. */ void add_config_stat(fn, statp) char *fn; /* name of file */ struct stat *statp; /* stat of the file, or NULL */ { struct config_stat *new = (struct config_stat *)xmalloc(sizeof(*new)); #ifdef EXTREME_DEBUGGING DEBUG2(DBG_CONF_HI, "add_config_stat(%s, %s) called\n", fn, statp ? "FOUND" : "MISSING"); #endif new->fn = COPY_STRING(fn); if (statp) { new->mtime = statp->st_mtime; new->dev = statp->st_dev; new->ino = statp->st_ino; } else { new->mtime = 0; } new->succ = config_stat_list; config_stat_list = new; } /* * is_newconf - check if we have a new set of configuration files * * returns TRUE if a configuration file has changed, FALSE otherwise */ int is_newconf() { struct stat statbuf; struct config_stat *csp; for (csp = config_stat_list; csp; csp = csp->succ) { if (stat(csp->fn, &statbuf) < 0) { if (csp->mtime != (time_t)0) { /* file no longer exists */ return TRUE; } } else { if (csp->mtime == (time_t)0) { /* file now exists */ return TRUE; } if (csp->mtime != statbuf.st_mtime || csp->dev != statbuf.st_dev || csp->ino != statbuf.st_ino) { /* existing file has changed */ return TRUE; } } } /* nothing has changed */ return FALSE; } #ifdef STANDALONE struct transport *transports; struct director *directors; struct router *routers; /* * find_direct_driver - given a driver's name, return the driver structure * * return NULL if driver does not exist. */ struct direct_driver * find_direct_driver(name) register char *name; /* search key */ { #ifdef notyet register struct direct_driver *ddp; /* pointer to table of drivers */ for (ddp = direct_drivers; ddp->name; ddp++) { if (EQ(ddp->name, name)) { return ddp; /* found the driver */ } } return NULL; /* driver not found */ #else /* notyet */ static struct direct_driver dd = { NULL, 1, NULL, NULL, NULL, NULL, }; return ⅆ #endif /* notyet */ } /* * find_transport_driver - given a driver's name, return the driver structure * * return NULL if driver does not exist. */ struct transport_driver * find_transport_driver(name) register char *name; /* search key */ { #ifdef notyet register struct transport_driver *tdp; /* pointer to table of drivers */ for (tdp = transport_drivers; tdp->name; tdp++) { if (EQ(tdp->name, name)) { return tdp; /* found the driver */ } } return NULL; /* driver not found */ #else /* notyet */ static struct transport_driver td = { NULL, 1, NULL, NULL, NULL, NULL, }; return &td; #endif /* notyet */ } /* * find_route_driver - given a driver's name, return the driver structure * * return NULL if driver does not exist. */ struct route_driver * find_route_driver(name) register char *name; /* search key */ { #ifdef notyet register struct route_driver *rdp; /* pointer to table of drivers */ for (rdp = route_drivers; rdp->name; rdp++) { if (EQ(rdp->name, name)) { return rdp; /* found the driver */ } } return NULL; /* driver not found */ #else /* notyet */ static struct route_driver rd = { NULL, 1, NULL, NULL, NULL, NULL, }; return &rd; #endif /* notyet */ } void main(argc, argv) int argc; /* count of arguments */ char *argv[]; /* vector of arguments */ { char *error; if (argc != 2) { (void) fprintf(stderr, "Usage: %s config-file", argv[0]); /*NOTREACHED*/ } config_file = argv[1]; if ((error = read_config_file(config_file, CONFIG_PRIMARY)) || (second_config_file && error = read_config_file(second_config_file, CONFIG_SECONDARY)) (error = read_transport_file()) || (error = read_router_file()) || (error = read_director_file()) || (error = read_qualify_file()) || (error = read_retry_file())) { (void) fprintf(stderr, "%s: %s\n", argv[0], error); } } #endif /* STANDALONE */ /* * Local Variables: * c-file-style: "smail" * End: */