X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fexim.c;h=198e81d892c43588841e270cb2f25c58780a841c;hb=9af3c5495821f33fc0bdd09375ff246120b6ca99;hp=354cdf7a807c41037e189d382579c407065c7ead;hpb=f9ba5e2255cf18092750fffacb6a9603571a2be5;p=exim.git diff --git a/src/src/exim.c b/src/src/exim.c index 354cdf7a8..198e81d89 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -187,7 +187,15 @@ DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info); va_end(ap); } +/*********************************************** +* Handler for SIGTERM * +***********************************************/ +static void +term_handler(int sig) +{ + exit(1); +} /************************************************* @@ -412,7 +420,7 @@ if (exim_tvcmp(&now_tv, then_tv) <= 0) DEBUG(D_transport|D_receive) { - if (!running_in_test_harness) + if (!f.running_in_test_harness) { debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", then_tv->tv_sec, (long) then_tv->tv_usec, @@ -542,9 +550,9 @@ close_unwanted(void) { if (smtp_input) { - #ifdef SUPPORT_TLS - tls_close(TRUE, FALSE); /* Shut down the TLS library */ - #endif +#ifdef SUPPORT_TLS + tls_close(NULL, TLS_NO_SHUTDOWN); /* Shut down the TLS library */ +#endif (void)close(fileno(smtp_in)); (void)close(fileno(smtp_out)); smtp_in = NULL; @@ -555,7 +563,7 @@ else if ((debug_selector & D_resolver) == 0) (void)close(1); /* stdout */ if (debug_selector == 0) /* stderr */ { - if (!synchronous_delivery) + if (!f.synchronous_delivery) { (void)close(2); log_stderr = NULL; @@ -601,21 +609,18 @@ if (euid == root_uid || euid != uid || egid != gid || igflag) if (igflag) { struct passwd *pw = getpwuid(uid); - if (pw != NULL) - { - if (initgroups(pw->pw_name, gid) != 0) - log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s", - (long int)uid, strerror(errno)); - } - else log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): " - "no passwd entry for uid=%ld", (long int)uid); + if (!pw) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): " + "no passwd entry for uid=%ld", (long int)uid); + + if (initgroups(pw->pw_name, gid) != 0) + log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s", + (long int)uid, strerror(errno)); } if (setgid(gid) < 0 || setuid(uid) < 0) - { log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to set gid=%ld or uid=%ld " "(euid=%ld): %s", (long int)gid, (long int)uid, (long int)euid, msg); - } } /* Debugging output included uid/gid and all groups */ @@ -670,6 +675,17 @@ exit(rc); +/* Print error string, then die */ +static void +exim_fail(const char * fmt, ...) +{ +va_list ap; +va_start(ap, fmt); +vfprintf(stderr, fmt, ap); +exit(EXIT_FAILURE); +} + + /************************************************* * Extract port from host address * @@ -691,10 +707,7 @@ check_port(uschar *address) { int port = host_address_extract_port(address); if (string_is_ip_address(address, NULL) == 0) - { - fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address); - exit(EXIT_FAILURE); - } + exim_fail("exim abandoned: \"%s\" is not an IP address\n", address); return port; } @@ -785,187 +798,193 @@ Returns: nothing */ static void -show_whats_supported(FILE * f) +show_whats_supported(FILE * fp) { auth_info * authi; -DEBUG(D_any) {} else show_db_version(f); +DEBUG(D_any) {} else show_db_version(fp); -fprintf(f, "Support for:"); +fprintf(fp, "Support for:"); #ifdef SUPPORT_CRYPTEQ - fprintf(f, " crypteq"); + fprintf(fp, " crypteq"); #endif #if HAVE_ICONV - fprintf(f, " iconv()"); + fprintf(fp, " iconv()"); #endif #if HAVE_IPV6 - fprintf(f, " IPv6"); + fprintf(fp, " IPv6"); #endif #ifdef HAVE_SETCLASSRESOURCES - fprintf(f, " use_setclassresources"); + fprintf(fp, " use_setclassresources"); #endif #ifdef SUPPORT_PAM - fprintf(f, " PAM"); + fprintf(fp, " PAM"); #endif #ifdef EXIM_PERL - fprintf(f, " Perl"); + fprintf(fp, " Perl"); #endif #ifdef EXPAND_DLFUNC - fprintf(f, " Expand_dlfunc"); + fprintf(fp, " Expand_dlfunc"); #endif #ifdef USE_TCP_WRAPPERS - fprintf(f, " TCPwrappers"); + fprintf(fp, " TCPwrappers"); #endif #ifdef SUPPORT_TLS # ifdef USE_GNUTLS - fprintf(f, " GnuTLS"); + fprintf(fp, " GnuTLS"); # else - fprintf(f, " OpenSSL"); + fprintf(fp, " OpenSSL"); # endif #endif #ifdef SUPPORT_TRANSLATE_IP_ADDRESS - fprintf(f, " translate_ip_address"); + fprintf(fp, " translate_ip_address"); #endif #ifdef SUPPORT_MOVE_FROZEN_MESSAGES - fprintf(f, " move_frozen_messages"); + fprintf(fp, " move_frozen_messages"); #endif #ifdef WITH_CONTENT_SCAN - fprintf(f, " Content_Scanning"); + fprintf(fp, " Content_Scanning"); +#endif +#ifdef SUPPORT_DANE + fprintf(fp, " DANE"); #endif #ifndef DISABLE_DKIM - fprintf(f, " DKIM"); + fprintf(fp, " DKIM"); #endif #ifndef DISABLE_DNSSEC - fprintf(f, " DNSSEC"); + fprintf(fp, " DNSSEC"); #endif #ifndef DISABLE_EVENT - fprintf(f, " Event"); + fprintf(fp, " Event"); #endif #ifdef SUPPORT_I18N - fprintf(f, " I18N"); + fprintf(fp, " I18N"); #endif #ifndef DISABLE_OCSP - fprintf(f, " OCSP"); + fprintf(fp, " OCSP"); #endif #ifndef DISABLE_PRDR - fprintf(f, " PRDR"); + fprintf(fp, " PRDR"); #endif #ifdef SUPPORT_PROXY - fprintf(f, " PROXY"); + fprintf(fp, " PROXY"); #endif #ifdef SUPPORT_SOCKS - fprintf(f, " SOCKS"); + fprintf(fp, " SOCKS"); #endif #ifdef SUPPORT_SPF - fprintf(f, " SPF"); + fprintf(fp, " SPF"); #endif #ifdef TCP_FASTOPEN deliver_init(); - if (tcp_fastopen_ok) fprintf(f, " TCP_Fast_Open"); + if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open"); #endif #ifdef EXPERIMENTAL_LMDB - fprintf(f, " Experimental_LMDB"); + fprintf(fp, " Experimental_LMDB"); #endif #ifdef EXPERIMENTAL_QUEUEFILE - fprintf(f, " Experimental_QUEUEFILE"); + fprintf(fp, " Experimental_QUEUEFILE"); #endif #ifdef EXPERIMENTAL_SRS - fprintf(f, " Experimental_SRS"); + fprintf(fp, " Experimental_SRS"); #endif -#ifdef EXPERIMENTAL_BRIGHTMAIL - fprintf(f, " Experimental_Brightmail"); +#ifdef EXPERIMENTAL_ARC + fprintf(fp, " Experimental_ARC"); #endif -#ifdef EXPERIMENTAL_DANE - fprintf(f, " Experimental_DANE"); +#ifdef EXPERIMENTAL_BRIGHTMAIL + fprintf(fp, " Experimental_Brightmail"); #endif #ifdef EXPERIMENTAL_DCC - fprintf(f, " Experimental_DCC"); + fprintf(fp, " Experimental_DCC"); #endif #ifdef EXPERIMENTAL_DMARC - fprintf(f, " Experimental_DMARC"); + fprintf(fp, " Experimental_DMARC"); #endif #ifdef EXPERIMENTAL_DSN_INFO - fprintf(f, " Experimental_DSN_info"); + fprintf(fp, " Experimental_DSN_info"); #endif -fprintf(f, "\n"); +#ifdef EXPERIMENTAL_REQUIRETLS + fprintf(fp, " Experimental_REQUIRETLS"); +#endif +fprintf(fp, "\n"); -fprintf(f, "Lookups (built-in):"); +fprintf(fp, "Lookups (built-in):"); #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2 - fprintf(f, " lsearch wildlsearch nwildlsearch iplsearch"); + fprintf(fp, " lsearch wildlsearch nwildlsearch iplsearch"); #endif #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2 - fprintf(f, " cdb"); + fprintf(fp, " cdb"); #endif #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2 - fprintf(f, " dbm dbmjz dbmnz"); + fprintf(fp, " dbm dbmjz dbmnz"); #endif #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2 - fprintf(f, " dnsdb"); + fprintf(fp, " dnsdb"); #endif #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2 - fprintf(f, " dsearch"); + fprintf(fp, " dsearch"); #endif #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2 - fprintf(f, " ibase"); + fprintf(fp, " ibase"); #endif #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 - fprintf(f, " ldap ldapdn ldapm"); + fprintf(fp, " ldap ldapdn ldapm"); #endif #ifdef EXPERIMENTAL_LMDB - fprintf(f, " lmdb"); + fprintf(fp, " lmdb"); #endif #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 - fprintf(f, " mysql"); + fprintf(fp, " mysql"); #endif #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2 - fprintf(f, " nis nis0"); + fprintf(fp, " nis nis0"); #endif #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2 - fprintf(f, " nisplus"); + fprintf(fp, " nisplus"); #endif #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2 - fprintf(f, " oracle"); + fprintf(fp, " oracle"); #endif #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2 - fprintf(f, " passwd"); + fprintf(fp, " passwd"); #endif #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 - fprintf(f, " pgsql"); + fprintf(fp, " pgsql"); #endif #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 - fprintf(f, " redis"); + fprintf(fp, " redis"); #endif #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2 - fprintf(f, " sqlite"); + fprintf(fp, " sqlite"); #endif #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2 - fprintf(f, " testdb"); + fprintf(fp, " testdb"); #endif #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2 - fprintf(f, " whoson"); + fprintf(fp, " whoson"); #endif -fprintf(f, "\n"); +fprintf(fp, "\n"); -auth_show_supported(f); -route_show_supported(f); -transport_show_supported(f); +auth_show_supported(fp); +route_show_supported(fp); +transport_show_supported(fp); #ifdef WITH_CONTENT_SCAN -malware_show_supported(f); +malware_show_supported(fp); #endif if (fixed_never_users[0] > 0) { int i; - fprintf(f, "Fixed never_users: "); + fprintf(fp, "Fixed never_users: "); for (i = 1; i <= (int)fixed_never_users[0] - 1; i++) - fprintf(f, "%d:", (unsigned int)fixed_never_users[i]); - fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]); + fprintf(fp, "%d:", (unsigned int)fixed_never_users[i]); + fprintf(fp, "%d\n", (unsigned int)fixed_never_users[i]); } -fprintf(f, "Configure owner: %d:%d\n", config_uid, config_gid); +fprintf(fp, "Configure owner: %d:%d\n", config_uid, config_gid); -fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); +fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); /* Everything else is details which are only worth reporting when debugging. Perhaps the tls_version_report should move into this too. */ @@ -975,9 +994,9 @@ DEBUG(D_any) do { /* clang defines __GNUC__ (at least, for me) so test for it first */ #if defined(__clang__) - fprintf(f, "Compiler: CLang [%s]\n", __clang_version__); + fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__); #elif defined(__GNUC__) - fprintf(f, "Compiler: GCC [%s]\n", + fprintf(fp, "Compiler: GCC [%s]\n", # ifdef __VERSION__ __VERSION__ # else @@ -985,29 +1004,29 @@ DEBUG(D_any) do { # endif ); #else - fprintf(f, "Compiler: \n"); + fprintf(fp, "Compiler: \n"); #endif #if defined(__GLIBC__) && !defined(__UCLIBC__) - fprintf(f, "Library version: Glibc: Compile: %d.%d\n", + fprintf(fp, "Library version: Glibc: Compile: %d.%d\n", __GLIBC__, __GLIBC_MINOR__); if (__GLIBC_PREREQ(2, 1)) - fprintf(f, " Runtime: %s\n", + fprintf(fp, " Runtime: %s\n", gnu_get_libc_version()); #endif -show_db_version(f); +show_db_version(fp); #ifdef SUPPORT_TLS - tls_version_report(f); + tls_version_report(fp); #endif #ifdef SUPPORT_I18N - utf8_version_report(f); + utf8_version_report(fp); #endif for (authi = auths_available; *authi->driver_name != '\0'; ++authi) if (authi->version_report) - (*authi->version_report)(f); + (*authi->version_report)(fp); /* PCRE_PRERELEASE is either defined and empty or a bare sequence of characters; unless it's an ancient version of PCRE in which case it @@ -1017,7 +1036,7 @@ show_db_version(f); #endif #define QUOTE(X) #X #define EXPAND_AND_QUOTE(X) QUOTE(X) - fprintf(f, "Library version: PCRE: Compile: %d.%d%s\n" + fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n" " Runtime: %s\n", PCRE_MAJOR, PCRE_MINOR, EXPAND_AND_QUOTE(PCRE_PRERELEASE) "", @@ -1028,17 +1047,17 @@ show_db_version(f); init_lookup_list(); for (i = 0; i < lookup_list_count; i++) if (lookup_list[i]->version_report) - lookup_list[i]->version_report(f); + lookup_list[i]->version_report(fp); #ifdef WHITELIST_D_MACROS - fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); + fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); #else - fprintf(f, "WHITELIST_D_MACROS unset\n"); + fprintf(fp, "WHITELIST_D_MACROS unset\n"); #endif #ifdef TRUSTED_CONFIG_LIST - fprintf(f, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); + fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); #else - fprintf(f, "TRUSTED_CONFIG_LIST unset\n"); + fprintf(fp, "TRUSTED_CONFIG_LIST unset\n"); #endif } while (0); @@ -1273,20 +1292,15 @@ exim_usage(uschar *progname) /* Handle specific program invocation variants */ if (Ustrcmp(progname, US"-mailq") == 0) - { - fprintf(stderr, + exim_fail( "mailq - list the contents of the mail queue\n\n" "For a list of options, see the Exim documentation.\n"); - exit(EXIT_FAILURE); - } /* Generic usage - we output this whatever happens */ -fprintf(stderr, +exim_fail( "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n" "not directly from a shell command line. Options and/or arguments control\n" "what it does when called. For a list of options, see the Exim documentation.\n"); - -exit(EXIT_FAILURE); } @@ -1295,32 +1309,6 @@ exit(EXIT_FAILURE); * Validate that the macros given are okay * *************************************************/ -#ifdef WHITELIST_D_MACROS -static void -wlist_check(uschar * name, uschar * val, void * ctx) -{ -uschar ** w, ** whites = ctx; -unsigned len; -int n; - -for (w = whites; *w; ++w) - if (Ustrcmp(*w, name) == 0) break; -if (*w) - { - if (!val || !*val) return; - len = Ustrlen(val); - if ((n = pcre_exec(regex_whitelisted_macro, NULL, CS val, len, - 0, PCRE_EOPT, NULL, 0)) >= 0) - return; - if (n != PCRE_ERROR_NOMATCH) - debug_printf("macros_trusted checking %s returned %d\n", name, n); - } -*whites = NULL; -return; -} -#endif - - /* Typically, Exim will drop privileges if macros are supplied. In some cases, we want to not do so. @@ -1333,7 +1321,7 @@ macros_trusted(BOOL opt_D_used) { #ifdef WHITELIST_D_MACROS macro_item *m; -uschar *whitelisted, *end, *p, **whites; +uschar *whitelisted, *end, *p, **whites, **w; int white_count, i, n; size_t len; BOOL prev_char_item, found; @@ -1396,9 +1384,32 @@ for (p = whitelisted, i = 0; (p != end) && (i < white_count); ++p) } whites[i] = NULL; -tree_walk(tree_macros, wlist_check, whites); -if (!*whites) return FALSE; - +/* The list of commandline macros should be very short. +Accept the N*M complexity. */ +for (m = macros_user; m; m = m->next) if (m->command_line) + { + found = FALSE; + for (w = whites; *w; ++w) + if (Ustrcmp(*w, m->name) == 0) + { + found = TRUE; + break; + } + if (!found) + return FALSE; + if (!m->replacement) + continue; + if ((len = m->replen) == 0) + continue; + n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len, + 0, PCRE_EOPT, NULL, 0); + if (n < 0) + { + if (n != PCRE_ERROR_NOMATCH) + debug_printf("macros_trusted checking %s returned %d\n", m->name, n); + return FALSE; + } + } DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n"); return TRUE; #endif @@ -1430,10 +1441,7 @@ len = Ustrlen(big_buffer); if (isupper(big_buffer[0])) { if (macro_read_assignment(big_buffer)) - { - uschar * s = Ustrchr(big_buffer, '='); - printf("Defined macro '%.*s'\n", (int)(s - big_buffer), big_buffer); - } + printf("Defined macro '%s'\n", mlast->name); } else if ((line = expand_string(big_buffer))) printf("%s\n", CS line); @@ -1441,6 +1449,7 @@ else } + /************************************************* * Entry point and high-level code * *************************************************/ @@ -1556,49 +1565,32 @@ This is a feature to make the lives of binary distributors easier. */ if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid)) { if (exim_uid == 0) - { - fprintf(stderr, "exim: refusing to run with uid 0 for \"%s\"\n", - EXIM_USERNAME); - exit(EXIT_FAILURE); - } + exim_fail("exim: refusing to run with uid 0 for \"%s\"\n", EXIM_USERNAME); + /* If ref:name uses a number as the name, route_finduser() returns TRUE with exim_uid set and pw coerced to NULL. */ if (pw) exim_gid = pw->pw_gid; #ifndef EXIM_GROUPNAME else - { - fprintf(stderr, + exim_fail( "exim: ref:name should specify a usercode, not a group.\n" "exim: can't let you get away with it unless you also specify a group.\n"); - exit(EXIT_FAILURE); - } #endif } else - { - fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n", - EXIM_USERNAME); - exit(EXIT_FAILURE); - } + exim_fail("exim: failed to find uid for user name \"%s\"\n", EXIM_USERNAME); #endif #ifdef EXIM_GROUPNAME if (!route_findgroup(US EXIM_GROUPNAME, &exim_gid)) - { - fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n", - EXIM_GROUPNAME); - exit(EXIT_FAILURE); - } + exim_fail("exim: failed to find gid for group name \"%s\"\n", EXIM_GROUPNAME); #endif #ifdef CONFIGURE_OWNERNAME if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid)) - { - fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n", + exim_fail("exim: failed to find uid for user name \"%s\"\n", CONFIGURE_OWNERNAME); - exit(EXIT_FAILURE); - } #endif /* We default the system_filter_user to be the Exim run-time user, as a @@ -1607,11 +1599,8 @@ system_filter_uid = exim_uid; #ifdef CONFIGURE_GROUPNAME if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid)) - { - fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n", + exim_fail("exim: failed to find gid for group name \"%s\"\n", CONFIGURE_GROUPNAME); - exit(EXIT_FAILURE); - } #endif /* In the Cygwin environment, some initialization used to need doing. @@ -1625,8 +1614,10 @@ OS_INIT /* Check a field which is patched when we are running Exim within its testing harness; do a fast initial check, and then the whole thing. */ -running_in_test_harness = +f.running_in_test_harness = *running_status == '<' && Ustrcmp(running_status, "<<>>") == 0; +if (f.running_in_test_harness) + debug_store = TRUE; /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed at the start of a program; however, it seems that some environments do not @@ -1643,10 +1634,7 @@ os_non_restarting_signal(SIGALRM, sigalrm_handler); because store_malloc writes a log entry on failure. */ if (!(log_buffer = US malloc(LOG_BUFFER_SIZE))) - { - fprintf(stderr, "exim: failed to get store for log buffer\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: failed to get store for log buffer\n"); /* Initialize the default log options. */ @@ -1679,6 +1667,10 @@ descriptive text. */ set_process_info("initializing"); os_restarting_signal(SIGUSR1, usr1_handler); +/* If running in a dockerized environment, the TERM signal is only +delegated to the PID 1 if we request it by setting an signal handler */ +if (getpid() == 1) signal(SIGTERM, term_handler); + /* SIGHUP is used to get the daemon to reconfigure. It gets set as appropriate in the daemon code. For the rest of Exim's uses, we ignore it. */ @@ -1785,7 +1777,7 @@ message has been sent). */ if ((namelen == 5 && Ustrcmp(argv[0], "rmail") == 0) || (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rmail", 6) == 0)) { - dot_ends = FALSE; + f.dot_ends = FALSE; called_as = US"-rmail"; errors_sender_rc = EXIT_SUCCESS; } @@ -1837,20 +1829,12 @@ real_gid = getgid(); if (real_uid == root_uid) { - rv = setgid(real_gid); - if (rv) - { - fprintf(stderr, "exim: setgid(%ld) failed: %s\n", + if ((rv = setgid(real_gid))) + exim_fail("exim: setgid(%ld) failed: %s\n", (long int)real_gid, strerror(errno)); - exit(EXIT_FAILURE); - } - rv = setuid(real_uid); - if (rv) - { - fprintf(stderr, "exim: setuid(%ld) failed: %s\n", + if ((rv = setuid(real_uid))) + exim_fail("exim: setuid(%ld) failed: %s\n", (long int)real_uid, strerror(errno)); - exit(EXIT_FAILURE); - } } /* If neither the original real uid nor the original euid was root, Exim is @@ -1907,7 +1891,7 @@ for (i = 1; i < argc; i++) { switchchar = arg[3]; argrest += 2; - queue_2stage = TRUE; + f.queue_2stage = TRUE; } /* Make -r synonymous with -f, since it is a documented alias */ @@ -1978,8 +1962,8 @@ for (i = 1; i < argc; i++) if (*argrest == 'd') { - daemon_listen = TRUE; - if (*(++argrest) == 'f') background_daemon = FALSE; + f.daemon_listen = TRUE; + if (*(++argrest) == 'f') f.background_daemon = FALSE; else if (*argrest != 0) { badarg = TRUE; break; } } @@ -2006,10 +1990,7 @@ for (i = 1; i < argc; i++) filter_test |= checking = FTEST_SYSTEM; if (*(++argrest) != 0) { badarg = TRUE; break; } if (++i < argc) filter_test_sfile = argv[i]; else - { - fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]); - exit(EXIT_FAILURE); - } + exim_fail("exim: file name expected after %s\n", argv[i-1]); } /* -bf: Run user filter test @@ -2025,18 +2006,12 @@ for (i = 1; i < argc; i++) { filter_test |= checking = FTEST_USER; if (++i < argc) filter_test_ufile = argv[i]; else - { - fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]); - exit(EXIT_FAILURE); - } + exim_fail("exim: file name expected after %s\n", argv[i-1]); } else { if (++i >= argc) - { - fprintf(stderr, "exim: string expected after %s\n", arg); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after %s\n", arg); if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i]; else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i]; else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i]; @@ -2051,8 +2026,8 @@ for (i = 1; i < argc; i++) { if (++i >= argc) { badarg = TRUE; break; } sender_host_address = argv[i]; - host_checking = checking = log_testing_mode = TRUE; - host_checking_callout = argrest[1] == 'c'; + host_checking = checking = f.log_testing_mode = TRUE; + f.host_checking_callout = argrest[1] == 'c'; message_logs = FALSE; } @@ -2109,8 +2084,8 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "nq") == 0) { - allow_unqualified_sender = FALSE; - allow_unqualified_recipient = FALSE; + f.allow_unqualified_sender = FALSE; + f.allow_unqualified_recipient = FALSE; } /* -bpxx: List the contents of the mail queue, in various forms. If @@ -2209,18 +2184,18 @@ for (i = 1; i < argc; i++) /* -bt: address testing mode */ else if (Ustrcmp(argrest, "t") == 0) - address_test_mode = checking = log_testing_mode = TRUE; + f.address_test_mode = checking = f.log_testing_mode = TRUE; /* -bv: verify addresses */ else if (Ustrcmp(argrest, "v") == 0) - verify_address_mode = checking = log_testing_mode = TRUE; + verify_address_mode = checking = f.log_testing_mode = TRUE; /* -bvs: verify sender addresses */ else if (Ustrcmp(argrest, "vs") == 0) { - verify_address_mode = checking = log_testing_mode = TRUE; + verify_address_mode = checking = f.log_testing_mode = TRUE; verify_as_sender = TRUE; } @@ -2233,25 +2208,19 @@ for (i = 1; i < argc; i++) printf("%s\n", CS version_copyright); version_printed = TRUE; show_whats_supported(stdout); - log_testing_mode = TRUE; + f.log_testing_mode = TRUE; } /* -bw: inetd wait mode, accept a listening socket as stdin */ else if (*argrest == 'w') { - inetd_wait_mode = TRUE; - background_daemon = FALSE; - daemon_listen = TRUE; + f.inetd_wait_mode = TRUE; + f.background_daemon = FALSE; + f.daemon_listen = TRUE; if (*(++argrest) != '\0') - { - inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE); - if (inetd_wait_timeout <= 0) - { - fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); - exit(EXIT_FAILURE); - } - } + if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); } else badarg = TRUE; @@ -2281,10 +2250,7 @@ for (i = 1; i < argc; i++) Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 || Ustrstr(filename, "/../") != NULL) && (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)) - { - fprintf(stderr, "-C Permission denied\n"); - exit(EXIT_FAILURE); - } + exim_fail("-C Permission denied\n"); } #endif if (real_uid != root_uid) @@ -2296,7 +2262,7 @@ for (i = 1; i < argc; i++) && real_uid != config_uid #endif ) - trusted_config = FALSE; + f.trusted_config = FALSE; else { FILE *trust_list = Ufopen(TRUSTED_CONFIG_LIST, "rb"); @@ -2318,7 +2284,7 @@ for (i = 1; i < argc; i++) ) || /* or */ (statbuf.st_mode & 2) != 0) /* world writeable */ { - trusted_config = FALSE; + f.trusted_config = FALSE; fclose(trust_list); } else @@ -2350,7 +2316,7 @@ for (i = 1; i < argc; i++) int sep = 0; const uschar *list = argrest; uschar *filename; - while (trusted_config && (filename = string_nextinlist(&list, + while (f.trusted_config && (filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) { for (i=0; i < nr_configs; i++) @@ -2360,7 +2326,7 @@ for (i = 1; i < argc; i++) } if (i == nr_configs) { - trusted_config = FALSE; + f.trusted_config = FALSE; break; } } @@ -2369,24 +2335,24 @@ for (i = 1; i < argc; i++) else { /* No valid prefixes found in trust_list file. */ - trusted_config = FALSE; + f.trusted_config = FALSE; } } } else { /* Could not open trust_list file. */ - trusted_config = FALSE; + f.trusted_config = FALSE; } } #else /* Not root; don't trust config */ - trusted_config = FALSE; + f.trusted_config = FALSE; #endif } config_main_filelist = argrest; - config_changed = TRUE; + f.config_changed = TRUE; } break; @@ -2394,10 +2360,9 @@ for (i = 1; i < argc; i++) /* -D: set up a macro definition */ case 'D': - #ifdef DISABLE_D_OPTION - fprintf(stderr, "exim: -D is not available in this Exim binary\n"); - exit(EXIT_FAILURE); - #else +#ifdef DISABLE_D_OPTION + exim_fail("exim: -D is not available in this Exim binary\n"); +#else { int ptr = 0; macro_item *m; @@ -2408,11 +2373,8 @@ for (i = 1; i < argc; i++) while (isspace(*s)) s++; if (*s < 'A' || *s > 'Z') - { - fprintf(stderr, "exim: macro name set by -D must start with " + exim_fail("exim: macro name set by -D must start with " "an upper case letter\n"); - exit(EXIT_FAILURE); - } while (isalnum(*s) || *s == '_') { @@ -2428,21 +2390,16 @@ for (i = 1; i < argc; i++) while (isspace(*s)) s++; } - if (macro_search(name)) - { - fprintf(stderr, "exim: duplicated -D in command line\n"); - exit(EXIT_FAILURE); - } + for (m = macros_user; m; m = m->next) + if (Ustrcmp(m->name, name) == 0) + exim_fail("exim: duplicated -D in command line\n"); m = macro_create(name, s, TRUE); if (clmacro_count >= MAX_CLMACROS) - { - fprintf(stderr, "exim: too many -D options on command line\n"); - exit(EXIT_FAILURE); - } - clmacros[clmacro_count++] = string_sprintf("-D%s=%s", - m->tnode.name, m->tnode.data.ptr); + exim_fail("exim: too many -D options on command line\n"); + clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name, + m->replacement); } #endif break; @@ -2467,7 +2424,7 @@ for (i = 1; i < argc; i++) debug_file = NULL; if (*argrest == 'd') { - debug_daemon = TRUE; + f.debug_daemon = TRUE; argrest++; } if (*argrest != 0) @@ -2486,7 +2443,7 @@ for (i = 1; i < argc; i++) message_reference at it, for logging. */ case 'E': - local_error_message = TRUE; + f.local_error_message = TRUE; if (mac_ismsgid(argrest)) message_reference = argrest; break; @@ -2523,7 +2480,7 @@ for (i = 1; i < argc; i++) { badarg = TRUE; break; } } originator_name = argrest; - sender_name_forced = TRUE; + f.sender_name_forced = TRUE; break; @@ -2571,13 +2528,10 @@ for (i = 1; i < argc; i++) #endif allow_domain_literals = FALSE; strip_trailing_dot = FALSE; - if (sender_address == NULL) - { - fprintf(stderr, "exim: bad -f address \"%s\": %s\n", argrest, errmess); - return EXIT_FAILURE; - } + if (!sender_address) + exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); } - sender_address_forced = TRUE; + f.sender_address_forced = TRUE; } break; @@ -2608,7 +2562,7 @@ for (i = 1; i < argc; i++) not to be documented for sendmail but mailx (at least) uses it) */ case 'i': - if (*argrest == 0) dot_ends = FALSE; else badarg = TRUE; + if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE; break; @@ -2621,17 +2575,10 @@ for (i = 1; i < argc; i++) if(++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } } - sz = Ustrlen(argrest); - if (sz > 32) - { - fprintf(stderr, "exim: the -L syslog name is too long: \"%s\"\n", argrest); - return EXIT_FAILURE; - } + if ((sz = Ustrlen(argrest)) > 32) + exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest); if (sz < 1) - { - fprintf(stderr, "exim: the -L syslog name is too short\n"); - return EXIT_FAILURE; - } + exim_fail("exim: the -L syslog name is too short\n"); cmdline_syslog_name = argrest; break; @@ -2657,16 +2604,10 @@ for (i = 1; i < argc; i++) EXIM_SOCKLEN_T size = sizeof(interface_sock); if (argc != i + 6) - { - fprintf(stderr, "exim: too many or too few arguments after -MC\n"); - return EXIT_FAILURE; - } + exim_fail("exim: too many or too few arguments after -MC\n"); if (msg_action_arg >= 0) - { - fprintf(stderr, "exim: incompatible arguments\n"); - return EXIT_FAILURE; - } + exim_fail("exim: incompatible arguments\n"); continue_transport = argv[++i]; continue_hostname = argv[++i]; @@ -2679,11 +2620,8 @@ for (i = 1; i < argc; i++) queue_run_pipe = passed_qr_pipe; if (!mac_ismsgid(argv[i])) - { - fprintf(stderr, "exim: malformed message id %s after -MC option\n", + exim_fail("exim: malformed message id %s after -MC option\n", argv[i]); - return EXIT_FAILURE; - } /* Set up $sending_ip_address and $sending_port, unless proxied */ @@ -2693,13 +2631,10 @@ for (i = 1; i < argc; i++) sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port); else - { - fprintf(stderr, "exim: getsockname() failed after -MC option: %s\n", + exim_fail("exim: getsockname() failed after -MC option: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - if (running_in_test_harness) millisleep(500); + if (f.running_in_test_harness) millisleep(500); break; } @@ -2711,7 +2646,7 @@ for (i = 1; i < argc; i++) precedes -MC (see above). The flag indicates that the host to which Exim is connected has accepted an AUTH sequence. */ - case 'A': smtp_authenticated = TRUE; break; + case 'A': f.smtp_authenticated = TRUE; break; /* -MCD: set the smtp_use_dsn flag; this indicates that the host that exim is connected to supports the esmtp extension DSN */ @@ -2771,9 +2706,19 @@ for (i = 1; i < argc; i++) default: badarg = TRUE; break; } - break; + break; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + /* -MS set REQUIRETLS on (new) message */ + + else if (*argrest == 'S') + { + tls_requiretls |= REQUIRETLS_MSG; + break; + } +#endif + /* -M[x]: various operations on the following list of message ids: -M deliver the messages, ignoring next retry times and thawing -Mc deliver the messages, checking next retry times, no thawing @@ -2798,7 +2743,7 @@ for (i = 1; i < argc; i++) else if (*argrest == 0) { msg_action = MSG_DELIVER; - forced_delivery = deliver_force_thaw = TRUE; + forced_delivery = f.deliver_force_thaw = TRUE; } else if (Ustrcmp(argrest, "ar") == 0) { @@ -2859,10 +2804,7 @@ for (i = 1; i < argc; i++) msg_action_arg = i + 1; if (msg_action_arg >= argc) - { - fprintf(stderr, "exim: no message ids given after %s option\n", arg); - return EXIT_FAILURE; - } + exim_fail("exim: no message ids given after %s option\n", arg); /* Some require only message ids to follow */ @@ -2870,11 +2812,8 @@ for (i = 1; i < argc; i++) { int j; for (j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j])) - { - fprintf(stderr, "exim: malformed message id %s after %s option\n", + exim_fail("exim: malformed message id %s after %s option\n", argv[j], arg); - return EXIT_FAILURE; - } goto END_ARG; /* Remaining args are ids */ } @@ -2884,11 +2823,8 @@ for (i = 1; i < argc; i++) else { if (!mac_ismsgid(argv[msg_action_arg])) - { - fprintf(stderr, "exim: malformed message id %s after %s option\n", + exim_fail("exim: malformed message id %s after %s option\n", argv[msg_action_arg], arg); - return EXIT_FAILURE; - } i++; } break; @@ -2908,7 +2844,7 @@ for (i = 1; i < argc; i++) case 'N': if (*argrest == 0) { - dont_deliver = TRUE; + f.dont_deliver = TRUE; debug_selector |= D_v; debug_file = stderr; } @@ -2932,10 +2868,7 @@ for (i = 1; i < argc; i++) if (*argrest == 0) { if (++i >= argc) - { - fprintf(stderr, "exim: string expected after -O\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after -O\n"); } break; @@ -2950,10 +2883,7 @@ for (i = 1; i < argc; i++) if (alias_arg[0] == 0) { if (i+1 < argc) alias_arg = argv[++i]; else - { - fprintf(stderr, "exim: string expected after -oA\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after -oA\n"); } } @@ -2974,10 +2904,7 @@ for (i = 1; i < argc; i++) if (p != NULL) { if (!isdigit(*p)) - { - fprintf(stderr, "exim: number expected after -oB\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: number expected after -oB\n"); connection_max_messages = Uatoi(p); } } @@ -2986,7 +2913,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "db") == 0) { - synchronous_delivery = FALSE; + f.synchronous_delivery = FALSE; arg_queue_only = FALSE; queue_only_set = TRUE; } @@ -2997,7 +2924,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "df") == 0 || Ustrcmp(argrest, "di") == 0) { - synchronous_delivery = TRUE; + f.synchronous_delivery = TRUE; arg_queue_only = FALSE; queue_only_set = TRUE; } @@ -3006,7 +2933,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "dq") == 0) { - synchronous_delivery = FALSE; + f.synchronous_delivery = FALSE; arg_queue_only = TRUE; queue_only_set = TRUE; } @@ -3016,7 +2943,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "dqs") == 0) { - queue_smtp = TRUE; + f.queue_smtp = TRUE; arg_queue_only = FALSE; queue_only_set = TRUE; } @@ -3030,7 +2957,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "i") == 0 || Ustrcmp(argrest, "itrue") == 0) - dot_ends = FALSE; + f.dot_ends = FALSE; /* -oM*: Set various characteristics for an incoming message; actually acted on for trusted callers only. */ @@ -3038,10 +2965,7 @@ for (i = 1; i < argc; i++) else if (*argrest == 'M') { if (i+1 >= argc) - { - fprintf(stderr, "exim: data expected after -o%s\n", argrest); - exit(EXIT_FAILURE); - } + exim_fail("exim: data expected after -o%s\n", argrest); /* -oMa: Set sender host address */ @@ -3069,15 +2993,9 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "Mm") == 0) { if (!mac_ismsgid(argv[i+1])) - { - fprintf(stderr,"-oMm must be a valid message ID\n"); - exit(EXIT_FAILURE); - } - if (!trusted_config) - { - fprintf(stderr,"-oMm must be called by a trusted user/config\n"); - exit(EXIT_FAILURE); - } + exim_fail("-oMm must be a valid message ID\n"); + if (!f.trusted_config) + exim_fail("-oMm must be called by a trusted user/config\n"); message_reference = argv[++i]; } @@ -3086,11 +3004,9 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "Mr") == 0) if (received_protocol) - { - fprintf(stderr, "received_protocol is set already\n"); - exit(EXIT_FAILURE); - } - else received_protocol = argv[++i]; + exim_fail("received_protocol is set already\n"); + else + received_protocol = argv[++i]; /* -oMs: Set sender host name */ @@ -3142,10 +3058,7 @@ for (i = 1; i < argc; i++) } else *tp = readconf_readtime(argrest + 1, 0, FALSE); if (*tp < 0) - { - fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); - exit(EXIT_FAILURE); - } + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); } /* -oX : Override local_interfaces and/or default daemon ports */ @@ -3189,10 +3102,7 @@ for (i = 1; i < argc; i++) uschar *hn; if (received_protocol) - { - fprintf(stderr, "received_protocol is set already\n"); - exit(EXIT_FAILURE); - } + exim_fail("received_protocol is set already\n"); hn = Ustrchr(argrest, ':'); if (hn == NULL) @@ -3212,16 +3122,13 @@ for (i = 1; i < argc; i++) case 'q': receiving_message = FALSE; if (queue_interval >= 0) - { - fprintf(stderr, "exim: -q specified more than once\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: -q specified more than once\n"); /* -qq...: Do queue runs in a 2-stage manner */ if (*argrest == 'q') { - queue_2stage = TRUE; + f.queue_2stage = TRUE; argrest++; } @@ -3229,7 +3136,7 @@ for (i = 1; i < argc; i++) if (*argrest == 'i') { - queue_run_first_delivery = TRUE; + f.queue_run_first_delivery = TRUE; argrest++; } @@ -3238,10 +3145,10 @@ for (i = 1; i < argc; i++) if (*argrest == 'f') { - queue_run_force = TRUE; + f.queue_run_force = TRUE; if (*++argrest == 'f') { - deliver_force_thaw = TRUE; + f.deliver_force_thaw = TRUE; argrest++; } } @@ -3250,7 +3157,7 @@ for (i = 1; i < argc; i++) if (*argrest == 'l') { - queue_run_local = TRUE; + f.queue_run_local = TRUE; argrest++; } @@ -3283,10 +3190,7 @@ for (i = 1; i < argc; i++) else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i], 0, FALSE)) <= 0) - { - fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); - exit(EXIT_FAILURE); - } + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); break; @@ -3308,9 +3212,9 @@ for (i = 1; i < argc; i++) for (i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { - if (i != 2) queue_run_force = TRUE; - if (i >= 2) deliver_selectstring_regex = TRUE; - if (i == 1 || i == 4) deliver_force_thaw = TRUE; + if (i != 2) f.queue_run_force = TRUE; + if (i >= 2) f.deliver_selectstring_regex = TRUE; + if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; argrest += Ustrlen(rsopts[i]); } } @@ -3323,10 +3227,7 @@ for (i = 1; i < argc; i++) else if (i+1 < argc) deliver_selectstring = argv[++i]; else - { - fprintf(stderr, "exim: string expected after -R\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after -R\n"); break; @@ -3353,9 +3254,9 @@ for (i = 1; i < argc; i++) for (i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { - if (i != 2) queue_run_force = TRUE; - if (i >= 2) deliver_selectstring_sender_regex = TRUE; - if (i == 1 || i == 4) deliver_force_thaw = TRUE; + if (i != 2) f.queue_run_force = TRUE; + if (i >= 2) f.deliver_selectstring_sender_regex = TRUE; + if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; argrest += Ustrlen(rsopts[i]); } } @@ -3368,10 +3269,7 @@ for (i = 1; i < argc; i++) else if (i+1 < argc) deliver_selectstring_sender = argv[++i]; else - { - fprintf(stderr, "exim: string expected after -S\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after -S\n"); break; /* -Tqt is an option that is exclusively for use by the testing suite. @@ -3380,7 +3278,7 @@ for (i = 1; i < argc; i++) tested. Otherwise variability of clock ticks etc. cause problems. */ case 'T': - if (running_in_test_harness && Ustrcmp(argrest, "qt") == 0) + if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) fudged_queue_times = argv[++i]; else badarg = TRUE; break; @@ -3397,7 +3295,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "i") == 0) { extract_recipients = TRUE; - dot_ends = FALSE; + f.dot_ends = FALSE; } /* -tls-on-connect: don't wait for STARTTLS (for old clients) */ @@ -3450,19 +3348,15 @@ for (i = 1; i < argc; i++) case 'X': if (*argrest == '\0') if (++i >= argc) - { - fprintf(stderr, "exim: string expected after -X\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: string expected after -X\n"); break; case 'z': if (*argrest == '\0') - if (++i < argc) log_oneline = argv[i]; else - { - fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]); - exit(EXIT_FAILURE); - } + if (++i < argc) + log_oneline = argv[i]; + else + exim_fail("exim: file name expected after %s\n", argv[i-1]); break; /* All other initial characters are errors */ @@ -3475,11 +3369,8 @@ for (i = 1; i < argc; i++) /* Failed to recognize the option, or syntax error */ if (badarg) - { - fprintf(stderr, "exim abandoned: unknown, malformed, or incomplete " + exim_fail("exim abandoned: unknown, malformed, or incomplete " "option %s\n", arg); - exit(EXIT_FAILURE); - } } @@ -3497,26 +3388,26 @@ if (usage_wanted) exim_usage(called_as); /* Arguments have been processed. Check for incompatibilities. */ if (( (smtp_input || extract_recipients || recipients_arg < argc) && - (daemon_listen || queue_interval >= 0 || bi_option || + (f.daemon_listen || queue_interval >= 0 || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0 || filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action)) ) || ( msg_action_arg > 0 && - (daemon_listen || queue_interval > 0 || list_options || + (f.daemon_listen || queue_interval > 0 || list_options || (checking && msg_action != MSG_LOAD) || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) ) || ( - (daemon_listen || queue_interval > 0) && + (f.daemon_listen || queue_interval > 0) && (sender_address != NULL || list_options || list_queue || checking || bi_option) ) || ( - daemon_listen && queue_interval == 0 + f.daemon_listen && queue_interval == 0 ) || ( - inetd_wait_mode && queue_interval >= 0 + f.inetd_wait_mode && queue_interval >= 0 ) || ( list_options && @@ -3525,11 +3416,11 @@ if (( ) || ( verify_address_mode && - (address_test_mode || smtp_input || extract_recipients || + (f.address_test_mode || smtp_input || extract_recipients || filter_test != FTEST_NONE || bi_option) ) || ( - address_test_mode && (smtp_input || extract_recipients || + f.address_test_mode && (smtp_input || extract_recipients || filter_test != FTEST_NONE || bi_option) ) || ( @@ -3544,10 +3435,7 @@ if (( (!expansion_test || expansion_test_message != NULL) ) ) - { - fprintf(stderr, "exim: incompatible command-line options or arguments\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: incompatible command-line options or arguments\n"); /* If debugging is set up, set the file and the file descriptor to pass on to child processes. It should, of course, be 2 for stderr. Also, force the daemon @@ -3557,8 +3445,8 @@ if (debug_selector != 0) { debug_file = stderr; debug_fd = fileno(debug_file); - background_daemon = FALSE; - if (running_in_test_harness) millisleep(100); /* lets caller finish */ + f.background_daemon = FALSE; + if (f.running_in_test_harness) millisleep(100); /* lets caller finish */ if (debug_selector != D_v) /* -v only doesn't show this */ { debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n", @@ -3644,12 +3532,8 @@ check on the additional groups for the admin user privilege - can't do that till after reading the config, which might specify the exim gid. Therefore, save the group list here first. */ -group_count = getgroups(NGROUPS_MAX, group_list); -if (group_count < 0) - { - fprintf(stderr, "exim: getgroups() failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } +if ((group_count = getgroups(NGROUPS_MAX, group_list)) < 0) + exim_fail("exim: getgroups() failed: %s\n", strerror(errno)); /* There is a fundamental difference in some BSD systems in the matter of groups. FreeBSD and BSDI are known to be different; NetBSD and OpenBSD are @@ -3667,14 +3551,8 @@ since you have to be root to run it, even if throwing away groups. Not being root here happens only in some unusual configurations. We just ignore the error. */ -if (setgroups(0, NULL) != 0) - { - if (setgroups(1, group_list) != 0 && !unprivileged) - { - fprintf(stderr, "exim: setgroups() failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - } +if (setgroups(0, NULL) != 0 && setgroups(1, group_list) != 0 && !unprivileged) + exim_fail("exim: setgroups() failed: %s\n", strerror(errno)); /* If the configuration file name has been altered by an argument on the command line (either a new file name or a macro definition) and the caller is @@ -3694,10 +3572,10 @@ values (such as the path name). If running in the test harness, pretend that configuration file changes and macro definitions haven't happened. */ if (( /* EITHER */ - (!trusted_config || /* Config changed, or */ + (!f.trusted_config || /* Config changed, or */ !macros_trusted(opt_D_used)) && /* impermissible macros and */ real_uid != root_uid && /* Not root, and */ - !running_in_test_harness /* Not fudged */ + !f.running_in_test_harness /* Not fudged */ ) || /* OR */ expansion_test /* expansion testing */ || /* OR */ @@ -3717,8 +3595,8 @@ if (( /* EITHER */ Note that if the invoker is Exim, the logs remain available. Messing with this causes unlogged successful deliveries. */ - if ((log_stderr != NULL) && (real_uid != exim_uid)) - really_exim = FALSE; + if (log_stderr && real_uid != exim_uid) + f.really_exim = FALSE; } /* Privilege is to be retained for the moment. It may be dropped later, @@ -3726,32 +3604,21 @@ depending on the job that this Exim process has been asked to do. For now, set the real uid to the effective so that subsequent re-execs of Exim are done by a privileged user. */ -else exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective"); +else + exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective"); /* If testing a filter, open the file(s) now, before wasting time doing other setups and reading the message. */ -if ((filter_test & FTEST_SYSTEM) != 0) - { - filter_sfd = Uopen(filter_test_sfile, O_RDONLY, 0); - if (filter_sfd < 0) - { - fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_sfile, +if (filter_test & FTEST_SYSTEM) + if ((filter_sfd = Uopen(filter_test_sfile, O_RDONLY, 0)) < 0) + exim_fail("exim: failed to open %s: %s\n", filter_test_sfile, strerror(errno)); - return EXIT_FAILURE; - } - } -if ((filter_test & FTEST_USER) != 0) - { - filter_ufd = Uopen(filter_test_ufile, O_RDONLY, 0); - if (filter_ufd < 0) - { - fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_ufile, +if (filter_test & FTEST_USER) + if ((filter_ufd = Uopen(filter_test_ufile, O_RDONLY, 0)) < 0) + exim_fail("exim: failed to open %s: %s\n", filter_test_ufile, strerror(errno)); - return EXIT_FAILURE; - } - } /* Initialise lookup_list If debugging, already called above via version reporting. @@ -3765,7 +3632,7 @@ This needs to happen before we read the main configuration. */ init_lookup_list(); #ifdef SUPPORT_I18N -if (running_in_test_harness) smtputf8_advertise_hosts = NULL; +if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL; #endif /* Read the main runtime configuration data; this gives up if there @@ -3814,17 +3681,17 @@ since some actions can be performed by non-admin users. Instead, set admin_user for later interrogation. */ if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid) - admin_user = TRUE; + f.admin_user = TRUE; else { int i, j; - for (i = 0; i < group_count && !admin_user; i++) + for (i = 0; i < group_count && !f.admin_user; i++) if (group_list[i] == exim_gid) - admin_user = TRUE; + f.admin_user = TRUE; else if (admin_groups) - for (j = 1; j <= (int)admin_groups[0] && !admin_user; j++) + for (j = 1; j <= (int)admin_groups[0] && !f.admin_user; j++) if (admin_groups[j] == group_list[i]) - admin_user = TRUE; + f.admin_user = TRUE; } /* Another group of privileged users are the trusted users. These are root, @@ -3833,32 +3700,30 @@ are permitted to specify sender_addresses with -f on the command line, and other message parameters as well. */ if (real_uid == root_uid || real_uid == exim_uid) - trusted_caller = TRUE; + f.trusted_caller = TRUE; else { int i, j; if (trusted_users) - for (i = 1; i <= (int)trusted_users[0] && !trusted_caller; i++) + for (i = 1; i <= (int)trusted_users[0] && !f.trusted_caller; i++) if (trusted_users[i] == real_uid) - trusted_caller = TRUE; + f.trusted_caller = TRUE; if (trusted_groups) - for (i = 1; i <= (int)trusted_groups[0] && !trusted_caller; i++) + for (i = 1; i <= (int)trusted_groups[0] && !f.trusted_caller; i++) if (trusted_groups[i] == real_gid) - trusted_caller = TRUE; - else for (j = 0; j < group_count && !trusted_caller; j++) + f.trusted_caller = TRUE; + else for (j = 0; j < group_count && !f.trusted_caller; j++) if (trusted_groups[i] == group_list[j]) - trusted_caller = TRUE; + f.trusted_caller = TRUE; } /* At this point, we know if the user is privileged and some command-line options become possibly impermissible, depending upon the configuration file. */ -if (checking && commandline_checks_require_admin && !admin_user) { - fprintf(stderr, "exim: those command-line flags are set to require admin\n"); - exit(EXIT_FAILURE); -} +if (checking && commandline_checks_require_admin && !f.admin_user) + exim_fail("exim: those command-line flags are set to require admin\n"); /* Handle the decoding of logging options. */ @@ -3878,39 +3743,28 @@ DEBUG(D_any) /* If domain literals are not allowed, check the sender address that was supplied with -f. Ditto for a stripped trailing dot. */ -if (sender_address != NULL) +if (sender_address) { if (sender_address[sender_address_domain] == '[' && !allow_domain_literals) - { - fprintf(stderr, "exim: bad -f address \"%s\": domain literals not " + exim_fail("exim: bad -f address \"%s\": domain literals not " "allowed\n", sender_address); - return EXIT_FAILURE; - } if (f_end_dot && !strip_trailing_dot) - { - fprintf(stderr, "exim: bad -f address \"%s.\": domain is malformed " + exim_fail("exim: bad -f address \"%s.\": domain is malformed " "(trailing dot not allowed)\n", sender_address); - return EXIT_FAILURE; - } } /* See if an admin user overrode our logging. */ -if (cmdline_syslog_name != NULL) - { - if (admin_user) +if (cmdline_syslog_name) + if (f.admin_user) { syslog_processname = cmdline_syslog_name; log_file_path = string_copy(CUS"syslog"); } else - { /* not a panic, non-privileged users should not be able to spam paniclog */ - fprintf(stderr, + exim_fail( "exim: you lack sufficient privilege to specify syslog process name\n"); - return EXIT_FAILURE; - } - } /* Paranoia check of maximum lengths of certain strings. There is a check on the length of the log file path in log.c, which will come into effect @@ -3940,7 +3794,7 @@ if (Ustrlen(syslog_processname) > 32) "syslog_processname is longer than 32 chars: aborting"); if (log_oneline) - if (admin_user) + if (f.admin_user) { log_write(0, LOG_MAIN, "%s", log_oneline); return EXIT_SUCCESS; @@ -3982,7 +3836,7 @@ this. We have to make a new environment if TZ is wrong, but don't bother if timestamps_utc is set, because then all times are in UTC anyway. */ if (timezone_string && strcmpic(timezone_string, US"UTC") == 0) - timestamps_utc = TRUE; + f.timestamps_utc = TRUE; else { uschar *envtz = US getenv("TZ"); @@ -4035,14 +3889,14 @@ Exim user", but it hasn't, because either the -D option set macros, or the trusted configuration file (when deliver_drop_privilege is false). */ if ( removed_privilege - && (!trusted_config || opt_D_used) + && (!f.trusted_config || opt_D_used) && real_uid == exim_uid) if (deliver_drop_privilege) - really_exim = TRUE; /* let logging work normally */ + f.really_exim = TRUE; /* let logging work normally */ else log_write(0, LOG_MAIN|LOG_PANIC, "exim user lost privilege for using %s option", - trusted_config? "-D" : "-C"); + f.trusted_config? "-D" : "-C"); /* Start up Perl interpreter if Perl support is configured and there is a perl_startup option, and the configuration or the command line specifies @@ -4056,12 +3910,8 @@ if (opt_perl_at_start && opt_perl_startup != NULL) { uschar *errstr; DEBUG(D_any) debug_printf("Starting Perl interpreter\n"); - errstr = init_perl(opt_perl_startup); - if (errstr != NULL) - { - fprintf(stderr, "exim: error in perl_startup code: %s\n", errstr); - return EXIT_FAILURE; - } + if ((errstr = init_perl(opt_perl_startup))) + exim_fail("exim: error in perl_startup code: %s\n", errstr); opt_perl_started = TRUE; } #endif /* EXIM_PERL */ @@ -4071,16 +3921,24 @@ a debugging feature for finding out what arguments certain MUAs actually use. Don't attempt it if logging is disabled, or if listing variables or if verifying/testing addresses or expansions. */ -if (((debug_selector & D_any) != 0 || LOGGING(arguments)) - && really_exim && !list_options && !checking) +if ( (debug_selector & D_any || LOGGING(arguments)) + && f.really_exim && !list_options && !checking) { int i; uschar *p = big_buffer; Ustrcpy(p, "cwd= (failed)"); - Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); + if (!initial_cwd) + p += 13; + else + { + Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); + p += 4 + Ustrlen(initial_cwd); + /* in case p is near the end and we don't provide enough space for + * string_format to be willing to write. */ + *p = '\0'; + } - while (*p) p++; (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc); while (*p) p++; for (i = 0; i < argc; i++) @@ -4151,8 +4009,7 @@ if (bi_option) (argv[1] == NULL)? US"" : argv[1]); execv(CS argv[0], (char *const *)argv); - fprintf(stderr, "exim: exec failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); + exim_fail("exim: exec failed: %s\n", strerror(errno)); } else { @@ -4165,8 +4022,8 @@ if (bi_option) configuration file. We leave these prints here to ensure that syslog setup, logfile setup, and so on has already happened. */ -if (trusted_caller) DEBUG(D_any) debug_printf("trusted user\n"); -if (admin_user) DEBUG(D_any) debug_printf("admin user\n"); +if (f.trusted_caller) DEBUG(D_any) debug_printf("trusted user\n"); +if (f.admin_user) DEBUG(D_any) debug_printf("admin user\n"); /* Only an admin user may start the daemon or force a queue run in the default configuration, but the queue run restriction can be relaxed. Only an admin @@ -4176,18 +4033,15 @@ passwords, etc. in lookup queries). Only an admin user may request a queue count. Only an admin user can use the test interface to scan for email (because Exim will be in the spool dir and able to look at mails). */ -if (!admin_user) +if (!f.admin_user) { BOOL debugset = (debug_selector & ~D_v) != 0; - if (deliver_give_up || daemon_listen || malware_test_file || + if (deliver_give_up || f.daemon_listen || malware_test_file || (count_queue && queue_list_requires_admin) || (list_queue && queue_list_requires_admin) || (queue_interval >= 0 && prod_requires_admin) || - (debugset && !running_in_test_harness)) - { - fprintf(stderr, "exim:%s permission denied\n", debugset? " debugging" : ""); - exit(EXIT_FAILURE); - } + (debugset && !f.running_in_test_harness)) + exim_fail("exim:%s permission denied\n", debugset? " debugging" : ""); } /* If the real user is not root or the exim uid, the argument for passing @@ -4198,20 +4052,17 @@ regression testing. */ if (real_uid != root_uid && real_uid != exim_uid && (continue_hostname != NULL || - (dont_deliver && - (queue_interval >= 0 || daemon_listen || msg_action_arg > 0) - )) && !running_in_test_harness) - { - fprintf(stderr, "exim: Permission denied\n"); - return EXIT_FAILURE; - } + (f.dont_deliver && + (queue_interval >= 0 || f.daemon_listen || msg_action_arg > 0) + )) && !f.running_in_test_harness) + exim_fail("exim: Permission denied\n"); /* If the caller is not trusted, certain arguments are ignored when running for real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF). Note that authority for performing certain actions on messages is tested in the queue_action() function. */ -if (!trusted_caller && !checking) +if (!f.trusted_caller && !checking) { sender_host_name = sender_host_address = interface_address = sender_ident = received_protocol = NULL; @@ -4234,16 +4085,13 @@ else /* If the caller is trusted, then they can use -G to suppress_local_fixups. */ if (flag_G) { - if (trusted_caller) + if (f.trusted_caller) { - suppress_local_fixups = suppress_local_fixups_default = TRUE; + f.suppress_local_fixups = f.suppress_local_fixups_default = TRUE; DEBUG(D_acl) debug_printf("suppress_local_fixups forced on by -G\n"); } else - { - fprintf(stderr, "exim: permission denied (-G requires a trusted user)\n"); - return EXIT_FAILURE; - } + exim_fail("exim: permission denied (-G requires a trusted user)\n"); } /* If an SMTP message is being received check to see if the standard input is a @@ -4271,18 +4119,15 @@ if (smtp_input) if (real_uid == root_uid || real_uid == exim_uid || interface_port < 1024) { - is_inetd = TRUE; + f.is_inetd = TRUE; sender_host_address = host_ntoa(-1, (struct sockaddr *)(&inetd_sock), NULL, &sender_host_port); if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Input from " "inetd is not supported when mua_wrapper is set"); } else - { - fprintf(stderr, + exim_fail( "exim: Permission denied (unprivileged user, unprivileged port)\n"); - return EXIT_FAILURE; - } } } } @@ -4294,7 +4139,7 @@ root. There will be further calls later for each message received. */ #ifdef LOAD_AVG_NEEDS_ROOT if (receiving_message && (queue_only_load >= 0 || - (is_inetd && smtp_load_reserve >= 0) + (f.is_inetd && smtp_load_reserve >= 0) )) { load_average = OS_GETLOADAVG(); @@ -4326,7 +4171,7 @@ to the state Exim usually runs in. */ if (!unprivileged && /* originally had root AND */ !removed_privilege && /* still got root AND */ - !daemon_listen && /* not starting the daemon */ + !f.daemon_listen && /* not starting the daemon */ queue_interval <= 0 && /* (either kind of daemon) */ ( /* AND EITHER */ deliver_drop_privilege || /* requested unprivileged */ @@ -4334,7 +4179,7 @@ if (!unprivileged && /* originally had root AND */ queue_interval < 0 && /* not running the queue */ (msg_action_arg < 0 || /* and */ msg_action != MSG_DELIVER) && /* not delivering and */ - (!checking || !address_test_mode) /* not address checking */ + (!checking || !f.address_test_mode) /* not address checking */ ) ) ) exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed"); @@ -4351,11 +4196,7 @@ else no need to complain then. */ if (rv == -1) if (!(unprivileged || removed_privilege)) - { - fprintf(stderr, - "exim: changing group failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } + exim_fail("exim: changing group failed: %s\n", strerror(errno)); else DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n", (long int)exim_gid, strerror(errno)); @@ -4416,6 +4257,12 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) int yield = EXIT_SUCCESS; set_process_info("acting on specified messages"); + /* ACL definitions may be needed when removing a message (-Mrm) because + event_action gets expanded */ + + if (msg_action == MSG_REMOVE) + readconf_rest(); + if (!one_msg_action) { for (i = msg_action_arg; i < argc; i++) @@ -4429,7 +4276,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) } /* We used to set up here to skip reading the ACL section, on - (msg_action_arg > 0 || (queue_interval == 0 && !daemon_listen) + (msg_action_arg > 0 || (queue_interval == 0 && !f.daemon_listen) Now, since the intro of the ${acl } expansion, ACL definitions may be needed in transports so we lost the optimisation. */ @@ -4625,13 +4472,13 @@ message. */ if (msg_action_arg > 0 && msg_action != MSG_LOAD) { - if (prod_requires_admin && !admin_user) + if (prod_requires_admin && !f.admin_user) { fprintf(stderr, "exim: Permission denied\n"); exim_exit(EXIT_FAILURE, US"main"); } set_process_info("delivering specified messages"); - if (deliver_give_up) forced_delivery = deliver_force_thaw = TRUE; + if (deliver_give_up) forced_delivery = f.deliver_force_thaw = TRUE; for (i = msg_action_arg; i < argc; i++) { int status; @@ -4658,7 +4505,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) /* If only a single queue run is requested, without SMTP listening, we can just turn into a queue runner, with an optional starting message id. */ -if (queue_interval == 0 && !daemon_listen) +if (queue_interval == 0 && !f.daemon_listen) { DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", (start_queue_run_id == NULL)? US"" : US" starting at ", @@ -4695,7 +4542,7 @@ for (i = 0;;) if (!originator_name) { - if (!sender_address || (!trusted_caller && filter_test == FTEST_NONE)) + if (!sender_address || (!f.trusted_caller && filter_test == FTEST_NONE)) { uschar *name = US pw->pw_gecos; uschar *amp = Ustrchr(name, '&'); @@ -4760,7 +4607,7 @@ for (i = 0;;) configuration specifies something to use. When running in the test harness, any setting of unknown_login overrides the actual name. */ -if (originator_login == NULL || running_in_test_harness) +if (originator_login == NULL || f.running_in_test_harness) { if (unknown_login != NULL) { @@ -4795,7 +4642,7 @@ returns. We leave this till here so that the originator_ fields are available for incoming messages via the daemon. The daemon cannot be run in mua_wrapper mode. */ -if (daemon_listen || inetd_wait_mode || queue_interval > 0) +if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0) { if (mua_wrapper) { @@ -4819,7 +4666,7 @@ originator_* variables set. */ if (test_rewrite_arg >= 0) { - really_exim = FALSE; + f.really_exim = FALSE; if (test_rewrite_arg >= argc) { printf("-brw needs an address argument\n"); @@ -4834,9 +4681,9 @@ unless a trusted caller supplies a sender address with -f, or is passing in the message via SMTP (inetd invocation or otherwise). */ if ((sender_address == NULL && !smtp_input) || - (!trusted_caller && filter_test == FTEST_NONE)) + (!f.trusted_caller && filter_test == FTEST_NONE)) { - sender_local = TRUE; + f.sender_local = TRUE; /* A trusted caller can supply authenticated_sender and authenticated_id via -oMas and -oMai and if so, they will already be set. Otherwise, force @@ -4869,14 +4716,14 @@ if ((!smtp_input && sender_address == NULL) || !checking)) /* Not running tests, including filter tests */ { sender_address = originator_login; - sender_address_forced = FALSE; + f.sender_address_forced = FALSE; sender_address_domain = 0; } } /* Remember whether an untrusted caller set the sender address */ -sender_set_untrusted = sender_address != originator_login && !trusted_caller; +f.sender_set_untrusted = sender_address != originator_login && !f.trusted_caller; /* Ensure that the sender address is fully qualified unless it is the empty address, which indicates an error message, or doesn't exist (root caller, smtp @@ -4895,7 +4742,7 @@ predicated upon the sender. If no arguments are given, read addresses from stdin. Set debug_level to at least D_v to get full output for address testing. */ -if (verify_address_mode || address_test_mode) +if (verify_address_mode || f.address_test_mode) { int exit_value = 0; int flags = vopt_qualify; @@ -4955,11 +4802,8 @@ if (expansion_test) if (msg_action_arg > 0 && msg_action == MSG_LOAD) { uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */ - if (!admin_user) - { - fprintf(stderr, "exim: permission denied\n"); - exit(EXIT_FAILURE); - } + if (!f.admin_user) + exim_fail("exim: permission denied\n"); message_id = argv[msg_action_arg]; (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id); if ((deliver_datafile = spool_open_datafile(message_id)) < 0) @@ -4976,11 +4820,8 @@ if (expansion_test) int save_stdin = dup(0); int fd = Uopen(expansion_test_message, O_RDONLY, 0); if (fd < 0) - { - fprintf(stderr, "exim: failed to open %s: %s\n", expansion_test_message, + exim_fail("exim: failed to open %s: %s\n", expansion_test_message, strerror(errno)); - return EXIT_FAILURE; - } (void) dup2(fd, 0); filter_test = FTEST_USER; /* Fudge to make it look like filter test */ message_ended = END_NOTENDED; @@ -4993,11 +4834,11 @@ if (expansion_test) /* Only admin users may see config-file macros this way */ - if (!admin_user) tree_macros = NULL; + if (!f.admin_user) macros_user = macros = mlast = NULL; /* Allow $recipients for this testing */ - enable_dollar_recipients = TRUE; + f.enable_dollar_recipients = TRUE; /* Expand command line items */ @@ -5047,7 +4888,7 @@ if (raw_active_hostname != NULL) uschar *nah = expand_string(raw_active_hostname); if (nah == NULL) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand \"%s\" " "(smtp_active_hostname): %s", raw_active_hostname, expand_string_message); @@ -5070,7 +4911,7 @@ if (host_checking) if (!sender_ident_set) { sender_ident = NULL; - if (running_in_test_harness && sender_host_port != 0 && + if (f.running_in_test_harness && sender_host_port != 0 && interface_address != NULL && interface_port != 0) verify_get_ident(1413); } @@ -5088,8 +4929,8 @@ if (host_checking) smtp_input = TRUE; smtp_in = stdin; smtp_out = stdout; - sender_local = FALSE; - sender_host_notsocket = TRUE; + f.sender_local = FALSE; + f.sender_host_notsocket = TRUE; debug_file = stderr; debug_fd = fileno(debug_file); fprintf(stdout, "\n**** SMTP testing session as if from host %s\n" @@ -5140,6 +4981,8 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input) { if (version_printed) { + if (Ustrchr(config_main_filelist, ':')) + printf("Configuration file search path is %s\n", config_main_filelist); printf("Configuration file is %s\n", config_main_filename); return EXIT_SUCCESS; } @@ -5171,11 +5014,11 @@ to override any SMTP queueing. */ if (mua_wrapper) { - synchronous_delivery = TRUE; + f.synchronous_delivery = TRUE; arg_error_handling = ERRORS_STDERR; remote_max_parallel = 1; deliver_drop_privilege = TRUE; - queue_smtp = FALSE; + f.queue_smtp = FALSE; queue_smtp_domains = NULL; #ifdef SUPPORT_I18N message_utf8_downconvert = -1; /* convert-if-needed */ @@ -5198,7 +5041,7 @@ if (!smtp_input) error_handling = arg_error_handling; logging being sent down the socket and make an identd call to get the sender_ident. */ -else if (is_inetd) +else if (f.is_inetd) { (void)fclose(stderr); exim_nullstd(); /* Re-open to /dev/null */ @@ -5213,18 +5056,18 @@ already been done (which it will have been for inetd). This caters for the case when it is forced by -oMa. However, we must flag that it isn't a socket, so that the test for IP options is skipped for -bs input. */ -if (sender_host_address != NULL && sender_fullhost == NULL) +if (sender_host_address && !sender_fullhost) { host_build_sender_fullhost(); set_process_info("handling incoming connection from %s via -oMa", sender_fullhost); - sender_host_notsocket = TRUE; + f.sender_host_notsocket = TRUE; } /* Otherwise, set the sender host as unknown except for inetd calls. This prevents host checking in the case of -bs not from inetd and also for -bS. */ -else if (!is_inetd) sender_host_unknown = TRUE; +else if (!f.is_inetd) f.sender_host_unknown = TRUE; /* If stdout does not exist, then dup stdin to stdout. This can happen if exim is started from inetd. In this case fd 0 will be set to the socket, @@ -5240,7 +5083,7 @@ batch/HELO/EHLO/AUTH/TLS. */ if (smtp_input) { - if (!is_inetd) set_process_info("accepting a local %sSMTP message from <%s>", + if (!f.is_inetd) set_process_info("accepting a local %sSMTP message from <%s>", smtp_batched_input? "batched " : "", (sender_address!= NULL)? sender_address : originator_login); } @@ -5267,10 +5110,7 @@ message! (For interactive SMTP, the check happens at MAIL FROM and an SMTP error code is given.) */ if ((!smtp_input || smtp_batched_input) && !receive_check_fs(0)) - { - fprintf(stderr, "exim: insufficient disk space\n"); - return EXIT_FAILURE; - } + exim_fail("exim: insufficient disk space\n"); /* If this is smtp input of any kind, real or batched, handle the start of the SMTP session. @@ -5337,7 +5177,7 @@ this is logically inconsistent. In other words, it doesn't like the paranoia. As a consequence of this, the waitpid() below is now excluded if we are sure that SIG_IGN works. */ -if (!synchronous_delivery) +if (!f.synchronous_delivery) { #ifdef SA_NOCLDWAIT struct sigaction act; @@ -5392,10 +5232,10 @@ while (more) if (smtp_batched_input && acl_not_smtp_start != NULL) { uschar *user_msg, *log_msg; - enable_dollar_recipients = TRUE; + f.enable_dollar_recipients = TRUE; (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, &user_msg, &log_msg); - enable_dollar_recipients = FALSE; + f.enable_dollar_recipients = FALSE; } /* Now get the data for the message */ @@ -5432,8 +5272,8 @@ while (more) /* These options cannot be changed dynamically for non-SMTP messages */ - active_local_sender_retain = local_sender_retain; - active_local_from_check = local_from_check; + f.active_local_sender_retain = local_sender_retain; + f.active_local_from_check = local_from_check; /* Save before any rewriting */ @@ -5486,7 +5326,7 @@ while (more) allow_utf8_domains = b; } #endif - if (domain == 0 && !allow_unqualified_recipient) + if (domain == 0 && !f.allow_unqualified_recipient) { recipient = NULL; errmess = US"unqualified recipient address not allowed"; @@ -5540,10 +5380,10 @@ while (more) if (acl_not_smtp_start) { uschar *user_msg, *log_msg; - enable_dollar_recipients = TRUE; + f.enable_dollar_recipients = TRUE; (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, &user_msg, &log_msg); - enable_dollar_recipients = FALSE; + f.enable_dollar_recipients = FALSE; } /* Pause for a while waiting for input. If none received in that time, @@ -5675,7 +5515,7 @@ while (more) are ignored. */ if (mua_wrapper) - local_queue_only = queue_only_policy = deliver_freeze = FALSE; + local_queue_only = f.queue_only_policy = f.deliver_freeze = FALSE; /* Log the queueing here, when it will get a message id attached, but not if queue_only is set (case 0). Case 1 doesn't happen here (too many @@ -5700,7 +5540,7 @@ while (more) } } - else if (queue_only_policy || deliver_freeze) + else if (f.queue_only_policy || f.deliver_freeze) cancel_cutthrough_connection(TRUE, US"no delivery; queueing"); /* Else do the delivery unless the ACL or local_scan() called for queue only @@ -5752,7 +5592,7 @@ while (more) /* In the parent, wait if synchronous delivery is required. This will always be the case in MUA wrapper mode. */ - if (synchronous_delivery) + if (f.synchronous_delivery) { int status; while (wait(&status) != pid);