X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fexim.c;h=198e81d892c43588841e270cb2f25c58780a841c;hb=9af3c5495821f33fc0bdd09375ff246120b6ca99;hp=ee7bfcd22fa6a0e1de4c1a4aaa1721120eb172f3;hpb=875512a36119423217802de1f79350e7fce1cd9b;p=exim.git diff --git a/src/src/exim.c b/src/src/exim.c index ee7bfcd22..198e81d89 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -12,7 +12,7 @@ Also a few functions that don't naturally fit elsewhere. */ #include "exim.h" -#ifdef __GLIBC__ +#if defined(__GLIBC__) && !defined(__UCLIBC__) # include #endif @@ -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); +} /************************************************* @@ -212,8 +220,7 @@ int fd; os_restarting_signal(sig, usr1_handler); -fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE); -if (fd < 0) +if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0) { /* If we are already running as the Exim user, try to create it in the current process (assuming spool_directory exists). Otherwise, if we are @@ -345,7 +352,7 @@ Arguments: Returns: -1, 0, or +1 */ -int +static int exim_tvcmp(struct timeval *t1, struct timeval *t2) { if (t1->tv_sec > t2->tv_sec) return +1; @@ -413,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, @@ -543,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; @@ -556,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; @@ -602,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 */ @@ -659,17 +663,29 @@ Returns: does not return */ void -exim_exit(int rc) +exim_exit(int rc, const uschar * process) { search_tidyup(); DEBUG(D_any) - debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d terminating with rc=%d " - ">>>>>>>>>>>>>>>>\n", (int)getpid(), rc); + debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d " + ">>>>>>>>>>>>>>>>\n", (int)getpid(), + process ? "(" : "", process, process ? ") " : "", rc); 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; } @@ -743,26 +756,26 @@ else * Show supported features * *************************************************/ -/* This function is called for -bV/--version and for -d to output the optional -features of the current Exim binary. - -Arguments: a FILE for printing -Returns: nothing -*/ - static void -show_whats_supported(FILE *f) +show_db_version(FILE * f) { - auth_info *authi; - #ifdef DB_VERSION_STRING -fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING); +DEBUG(D_any) + { + fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); + fprintf(f, " Runtime: %s\n", + db_version(NULL, NULL, NULL)); + } +else + fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING); + #elif defined(BTREEVERSION) && defined(HASHVERSION) #ifdef USE_DB fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n"); #else fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n"); #endif + #elif defined(_DBM_RDONLY) || defined(dbm_dirfno) fprintf(f, "Probably ndbm\n"); #elif defined(USE_TDB) @@ -774,254 +787,204 @@ fprintf(f, "Using tdb\n"); fprintf(f, "Probably GDBM (compatibility mode)\n"); #endif #endif +} + + +/* This function is called for -bV/--version and for -d to output the optional +features of the current Exim binary. + +Arguments: a FILE for printing +Returns: nothing +*/ -fprintf(f, "Support for:"); +static void +show_whats_supported(FILE * fp) +{ +auth_info * authi; + +DEBUG(D_any) {} else show_db_version(fp); + +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"); - #else - fprintf(f, " OpenSSL"); - #endif +# ifdef USE_GNUTLS + fprintf(fp, " GnuTLS"); +# else + 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(fp, " SPF"); #endif #ifdef TCP_FASTOPEN - fprintf(f, " TCP_Fast_Open"); + deliver_init(); + 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"); -#endif -#ifdef EXPERIMENTAL_SPF - fprintf(f, " Experimental_SPF"); + 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 +#ifdef EXPERIMENTAL_REQUIRETLS + fprintf(fp, " Experimental_REQUIRETLS"); #endif -fprintf(f, "\n"); +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"); -fprintf(f, "Authenticators:"); -#ifdef AUTH_CRAM_MD5 - fprintf(f, " cram_md5"); -#endif -#ifdef AUTH_CYRUS_SASL - fprintf(f, " cyrus_sasl"); -#endif -#ifdef AUTH_DOVECOT - fprintf(f, " dovecot"); -#endif -#ifdef AUTH_GSASL - fprintf(f, " gsasl"); -#endif -#ifdef AUTH_HEIMDAL_GSSAPI - fprintf(f, " heimdal_gssapi"); -#endif -#ifdef AUTH_PLAINTEXT - fprintf(f, " plaintext"); -#endif -#ifdef AUTH_SPA - fprintf(f, " spa"); -#endif -#ifdef AUTH_TLS - fprintf(f, " tls"); -#endif -fprintf(f, "\n"); +auth_show_supported(fp); +route_show_supported(fp); +transport_show_supported(fp); -fprintf(f, "Routers:"); -#ifdef ROUTER_ACCEPT - fprintf(f, " accept"); -#endif -#ifdef ROUTER_DNSLOOKUP - fprintf(f, " dnslookup"); -#endif -#ifdef ROUTER_IPLITERAL - fprintf(f, " ipliteral"); -#endif -#ifdef ROUTER_IPLOOKUP - fprintf(f, " iplookup"); -#endif -#ifdef ROUTER_MANUALROUTE - fprintf(f, " manualroute"); -#endif -#ifdef ROUTER_QUERYPROGRAM - fprintf(f, " queryprogram"); -#endif -#ifdef ROUTER_REDIRECT - fprintf(f, " redirect"); -#endif -fprintf(f, "\n"); - -fprintf(f, "Transports:"); -#ifdef TRANSPORT_APPENDFILE - fprintf(f, " appendfile"); - #ifdef SUPPORT_MAILDIR - fprintf(f, "/maildir"); - #endif - #ifdef SUPPORT_MAILSTORE - fprintf(f, "/mailstore"); - #endif - #ifdef SUPPORT_MBX - fprintf(f, "/mbx"); - #endif -#endif -#ifdef TRANSPORT_AUTOREPLY - fprintf(f, " autoreply"); -#endif -#ifdef TRANSPORT_LMTP - fprintf(f, " lmtp"); -#endif -#ifdef TRANSPORT_PIPE - fprintf(f, " pipe"); -#endif -#ifdef EXPERIMENTAL_QUEUEFILE - fprintf(f, " queuefile"); -#endif -#ifdef TRANSPORT_SMTP - fprintf(f, " smtp"); +#ifdef WITH_CONTENT_SCAN +malware_show_supported(fp); #endif -fprintf(f, "\n"); 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. */ @@ -1031,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 @@ -1041,27 +1004,29 @@ DEBUG(D_any) do { # endif ); #else - fprintf(f, "Compiler: \n"); + fprintf(fp, "Compiler: \n"); #endif -#ifdef __GLIBC__ - fprintf(f, "Library version: Glibc: Compile: %d.%d\n", +#if defined(__GLIBC__) && !defined(__UCLIBC__) + 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(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 @@ -1071,7 +1036,7 @@ DEBUG(D_any) do { #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) "", @@ -1082,17 +1047,17 @@ DEBUG(D_any) do { 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); @@ -1119,8 +1084,8 @@ switch(request) "If the string is not recognised, you'll get this help (on stderr).\n" "\n" " exim -bI:help this information\n" -" exim -bI:dscp dscp value keywords known\n" -" exim -bI:sieve list of supported sieve extensions, one per line.\n" +" exim -bI:dscp list of known dscp value keywords\n" +" exim -bI:sieve list of supported sieve extensions\n" ); return; case CMDINFO_SIEVE: @@ -1150,8 +1115,7 @@ uschar * local_part_quote(uschar *lpart) { BOOL needs_quote = FALSE; -int size, ptr; -uschar *yield; +gstring * g; uschar *t; for (t = lpart; !needs_quote && *t != 0; t++) @@ -1162,26 +1126,24 @@ for (t = lpart; !needs_quote && *t != 0; t++) if (!needs_quote) return lpart; -size = ptr = 0; -yield = string_catn(NULL, &size, &ptr, US"\"", 1); +g = string_catn(NULL, US"\"", 1); for (;;) { uschar *nq = US Ustrpbrk(lpart, "\\\""); if (nq == NULL) { - yield = string_cat(yield, &size, &ptr, lpart); + g = string_cat(g, lpart); break; } - yield = string_catn(yield, &size, &ptr, lpart, nq - lpart); - yield = string_catn(yield, &size, &ptr, US"\\", 1); - yield = string_catn(yield, &size, &ptr, nq, 1); + g = string_catn(g, lpart, nq - lpart); + g = string_catn(g, US"\\", 1); + g = string_catn(g, nq, 1); lpart = nq + 1; } -yield = string_catn(yield, &size, &ptr, US"\"", 1); -yield[ptr] = 0; -return yield; +g = string_catn(g, US"\"", 1); +return string_from_gstring(g); } @@ -1254,11 +1216,9 @@ static uschar * get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *)) { int i; -int size = 0; -int ptr = 0; -uschar *yield = NULL; +gstring * g = NULL; -if (fn_readline == NULL) { printf("> "); fflush(stdout); } +if (!fn_readline) { printf("> "); fflush(stdout); } for (i = 0;; i++) { @@ -1293,23 +1253,22 @@ for (i = 0;; i++) while (p < ss && isspace(*p)) p++; /* leading space after cont */ } - yield = string_catn(yield, &size, &ptr, p, ss - p); + g = string_catn(g, p, ss - p); #ifdef USE_READLINE - if (fn_readline != NULL) free(readline_line); + if (fn_readline) free(readline_line); #endif - /* yield can only be NULL if ss==p */ - if (ss == p || yield[ptr-1] != '\\') - { - if (yield) yield[ptr] = 0; + /* g can only be NULL if ss==p */ + if (ss == p || g->s[g->ptr-1] != '\\') break; - } - yield[--ptr] = 0; + + --g->ptr; + (void) string_from_gstring(g); } -if (yield == NULL) printf("\n"); -return yield; +if (!g) printf("\n"); +return string_from_gstring(g); } @@ -1333,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); } @@ -1432,7 +1386,7 @@ whites[i] = NULL; /* The list of commandline macros should be very short. Accept the N*M complexity. */ -for (m = macros; m; m = m->next) if (m->command_line) +for (m = macros_user; m; m = m->next) if (m->command_line) { found = FALSE; for (w = whites; *w; ++w) @@ -1443,10 +1397,9 @@ for (m = macros; m; m = m->next) if (m->command_line) } if (!found) return FALSE; - if (m->replacement == NULL) + if (!m->replacement) continue; - len = Ustrlen(m->replacement); - if (len == 0) + if ((len = m->replen) == 0) continue; n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len, 0, PCRE_EOPT, NULL, 0); @@ -1463,6 +1416,40 @@ return TRUE; } +/************************************************* +* Expansion testing * +*************************************************/ + +/* Expand and print one item, doing macro-processing. + +Arguments: + item line for expansion +*/ + +static void +expansion_test_line(uschar * line) +{ +int len; +BOOL dummy_macexp; + +Ustrncpy(big_buffer, line, big_buffer_size); +big_buffer[big_buffer_size-1] = '\0'; +len = Ustrlen(big_buffer); + +(void) macros_expand(0, &len, &dummy_macexp); + +if (isupper(big_buffer[0])) + { + if (macro_read_assignment(big_buffer)) + printf("Defined macro '%s'\n", mlast->name); + } +else + if ((line = expand_string(big_buffer))) printf("%s\n", CS line); + else printf("Failed: %s\n", expand_string_message); +} + + + /************************************************* * Entry point and high-level code * *************************************************/ @@ -1578,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 @@ -1629,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. @@ -1647,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 @@ -1665,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. */ @@ -1701,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. */ @@ -1807,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; } @@ -1859,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 @@ -1929,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 */ @@ -2000,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; } } @@ -2028,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 @@ -2047,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]; @@ -2073,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; } @@ -2131,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 @@ -2231,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; } @@ -2255,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; @@ -2303,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) @@ -2318,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"); @@ -2340,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 @@ -2372,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++) @@ -2382,7 +2326,7 @@ for (i = 1; i < argc; i++) } if (i == nr_configs) { - trusted_config = FALSE; + f.trusted_config = FALSE; break; } } @@ -2391,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; @@ -2416,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; @@ -2430,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 == '_') { @@ -2450,20 +2390,14 @@ for (i = 1; i < argc; i++) while (isspace(*s)) s++; } - for (m = macros; m; m = m->next) + for (m = macros_user; m; m = m->next) if (Ustrcmp(m->name, name) == 0) - { - fprintf(stderr, "exim: duplicated -D in command line\n"); - exit(EXIT_FAILURE); - } + exim_fail("exim: duplicated -D in command line\n"); - m = macro_create(name, s, TRUE, FALSE); + 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); - } + exim_fail("exim: too many -D options on command line\n"); clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name, m->replacement); } @@ -2490,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) @@ -2509,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; @@ -2546,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; @@ -2594,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; @@ -2631,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; @@ -2644,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; @@ -2680,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]; @@ -2702,27 +2620,21 @@ 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 */ - if (!continue_proxy) + if (!continue_proxy_cipher) if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock), &size) == 0) 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; } @@ -2734,12 +2646,12 @@ 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 */ - case 'D': smtp_peer_options |= PEER_OFFERED_DSN; break; + case 'D': smtp_peer_options |= OPTION_DSN; break; /* -MCG: set the queue name, to a non-default value */ @@ -2749,12 +2661,12 @@ for (i = 1; i < argc; i++) /* -MCK: the peer offered CHUNKING. Must precede -MC */ - case 'K': smtp_peer_options |= PEER_OFFERED_CHUNKING; break; + case 'K': smtp_peer_options |= OPTION_CHUNKING; break; /* -MCP: set the smtp_use_pipelining flag; this is useful only when it preceded -MC (see above) */ - case 'P': smtp_peer_options |= PEER_OFFERED_PIPE; break; + case 'P': smtp_peer_options |= OPTION_PIPE; break; /* -MCQ: pass on the pid of the queue-running process that started this chain of deliveries and the fd of its synchronizing pipe; this @@ -2769,32 +2681,44 @@ for (i = 1; i < argc; i++) /* -MCS: set the smtp_use_size flag; this is useful only when it precedes -MC (see above) */ - case 'S': smtp_peer_options |= PEER_OFFERED_SIZE; break; + case 'S': smtp_peer_options |= OPTION_SIZE; break; #ifdef SUPPORT_TLS /* -MCt: similar to -MCT below but the connection is still open via a proxy proces which handles the TLS context and coding. - Require two arguments for the proxied local address and port. */ + Require three arguments for the proxied local address and port, + and the TLS cipher. */ - case 't': continue_proxy = TRUE; - if (++i < argc) sending_ip_address = argv[i]; + case 't': if (++i < argc) sending_ip_address = argv[i]; else badarg = TRUE; if (++i < argc) sending_port = (int)(Uatol(argv[i])); else badarg = TRUE; + if (++i < argc) continue_proxy_cipher = argv[i]; + else badarg = TRUE; /*FALLTHROUGH*/ /* -MCT: set the tls_offered flag; this is useful only when it precedes -MC (see above). The flag indicates that the host to which Exim is connected has offered TLS support. */ - case 'T': smtp_peer_options |= PEER_OFFERED_TLS; break; + case 'T': smtp_peer_options |= OPTION_TLS; break; #endif 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 @@ -2819,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) { @@ -2880,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 */ @@ -2891,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 */ } @@ -2905,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; @@ -2929,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; } @@ -2953,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; @@ -2971,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"); } } @@ -2995,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); } } @@ -3007,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; } @@ -3018,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; } @@ -3027,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; } @@ -3037,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; } @@ -3051,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. */ @@ -3059,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 */ @@ -3090,21 +2993,20 @@ 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]; } /* -oMr: Received protocol */ - else if (Ustrcmp(argrest, "Mr") == 0) received_protocol = argv[++i]; + else if (Ustrcmp(argrest, "Mr") == 0) + + if (received_protocol) + exim_fail("received_protocol is set already\n"); + else + received_protocol = argv[++i]; /* -oMs: Set sender host name */ @@ -3156,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 */ @@ -3193,18 +3092,21 @@ for (i = 1; i < argc; i++) which sets the host protocol and host name */ if (*argrest == 0) - { - if (i+1 < argc) argrest = argv[++i]; else + if (i+1 < argc) + argrest = argv[++i]; + else { badarg = TRUE; break; } - } if (*argrest != 0) { - uschar *hn = Ustrchr(argrest, ':'); + uschar *hn; + + if (received_protocol) + exim_fail("received_protocol is set already\n"); + + hn = Ustrchr(argrest, ':'); if (hn == NULL) - { received_protocol = argrest; - } else { int old_pool = store_pool; @@ -3220,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++; } @@ -3237,7 +3136,7 @@ for (i = 1; i < argc; i++) if (*argrest == 'i') { - queue_run_first_delivery = TRUE; + f.queue_run_first_delivery = TRUE; argrest++; } @@ -3246,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++; } } @@ -3258,7 +3157,7 @@ for (i = 1; i < argc; i++) if (*argrest == 'l') { - queue_run_local = TRUE; + f.queue_run_local = TRUE; argrest++; } @@ -3291,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; @@ -3316,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]); } } @@ -3331,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; @@ -3361,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]); } } @@ -3376,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. @@ -3388,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; @@ -3405,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) */ @@ -3458,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 */ @@ -3483,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); - } } @@ -3505,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 && @@ -3533,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) ) || ( @@ -3552,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 @@ -3565,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", @@ -3652,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 @@ -3675,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 @@ -3702,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 */ @@ -3725,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, @@ -3734,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. @@ -3773,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 @@ -3784,12 +3643,9 @@ NOTE: immediatly after opening the configuration file we change the working directory to "/"! Later we change to $spool_directory. We do it there, because during readconf_main() some expansion takes place already. */ -/* Store the initial cwd before we change directories */ -if ((initial_cwd = os_getcwd(NULL, 0)) == NULL) - { - perror("exim: can't get the current working directory"); - exit(EXIT_FAILURE); - } +/* Store the initial cwd before we change directories. Can be NULL if the +dir has already been unlinked. */ +initial_cwd = os_getcwd(NULL, 0); /* checking: -be[m] expansion test - @@ -3809,6 +3665,7 @@ defined) */ readconf_main(checking || list_options); + /* Now in directory "/" */ if (cleanup_environment() == FALSE) @@ -3824,21 +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; i++) - { - if (group_list[i] == exim_gid) admin_user = TRUE; - else if (admin_groups != NULL) - { - for (j = 1; j <= (int)(admin_groups[0]); j++) + for (i = 0; i < group_count && !f.admin_user; i++) + if (group_list[i] == exim_gid) + f.admin_user = TRUE; + else if (admin_groups) + for (j = 1; j <= (int)admin_groups[0] && !f.admin_user; j++) if (admin_groups[j] == group_list[i]) - { admin_user = TRUE; break; } - } - if (admin_user) break; - } + f.admin_user = TRUE; } /* Another group of privileged users are the trusted users. These are root, @@ -3847,34 +3700,31 @@ 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 != NULL) - { - for (i = 1; i <= (int)(trusted_users[0]); i++) + if (trusted_users) + for (i = 1; i <= (int)trusted_users[0] && !f.trusted_caller; i++) if (trusted_users[i] == real_uid) - { trusted_caller = TRUE; break; } - } + f.trusted_caller = TRUE; - if (!trusted_caller && trusted_groups != NULL) - { - for (i = 1; i <= (int)(trusted_groups[0]); i++) - { + if (trusted_groups) + 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; 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; break; } - } - if (trusted_caller) break; - } - } + 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 && !f.admin_user) + exim_fail("exim: those command-line flags are set to require admin\n"); + /* Handle the decoding of logging options. */ decode_bits(log_selector, log_selector_size, log_notall, @@ -3893,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 @@ -3955,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; @@ -3997,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"); @@ -4050,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 @@ -4071,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 */ @@ -4086,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++) @@ -4117,9 +3960,8 @@ if (((debug_selector & D_any) != 0 || LOGGING(arguments)) quote = US""; while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; } } - sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size - + p += sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size - (p - big_buffer) - 4), printing, quote); - while (*p) p++; } if (LOGGING(arguments)) @@ -4140,6 +3982,7 @@ if (Uchdir(spool_directory) != 0) int dummy; (void)directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, FALSE); dummy = /* quieten compiler */ Uchdir(spool_directory); + dummy = dummy; /* yet more compiler quietening, sigh */ } /* Handle calls with the -bi option. This is a sendmail option to rebuild *the* @@ -4166,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 { @@ -4180,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 @@ -4191,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 @@ -4213,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; @@ -4249,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 @@ -4286,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; - } } } } @@ -4309,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(); @@ -4341,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 */ @@ -4349,12 +4179,9 @@ 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"); - } /* When we are retaining a privileged uid, we still change to the exim gid. */ @@ -4368,17 +4195,11 @@ else there's no security risk. For me, it's { exim -bV } on a just-built binary, 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)); - } } /* Handle a request to scan a file for malware */ @@ -4436,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++) @@ -4449,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. */ @@ -4479,7 +4306,7 @@ if (test_retry_arg >= 0) if (test_retry_arg >= argc) { printf("-brt needs a domain or address argument\n"); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } s1 = argv[test_retry_arg++]; s2 = NULL; @@ -4528,8 +4355,9 @@ if (test_retry_arg >= 0) } } - yield = retry_find_config(s1, s2, basic_errno, more_errno); - if (yield == NULL) printf("No retry information found\n"); else + if (!(yield = retry_find_config(s1, s2, basic_errno, more_errno))) + printf("No retry information found\n"); + else { retry_rule *r; more_errno = yield->more_errno; @@ -4561,7 +4389,7 @@ if (test_retry_arg >= 0) printf("auth_failed "); else printf("* "); - for (r = yield->rules; r != NULL; r = r->next) + for (r = yield->rules; r; r = r->next) { printf("%c,%s", r->rule, readconf_printtime(r->timeout)); /* Do not */ printf(",%s", readconf_printtime(r->p1)); /* amalgamate */ @@ -4584,7 +4412,7 @@ if (test_retry_arg >= 0) printf("\n"); } - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } /* Handle a request to list one or more configuration options */ @@ -4592,30 +4420,33 @@ if (test_retry_arg >= 0) if (list_options) { + BOOL fail = FALSE; set_process_info("listing variables"); - if (recipients_arg >= argc) readconf_print(US"all", NULL, flag_n); - else for (i = recipients_arg; i < argc; i++) + if (recipients_arg >= argc) + fail = !readconf_print(US"all", NULL, flag_n); + else for (i = recipients_arg; i < argc; i++) + { + if (i < argc - 1 && + (Ustrcmp(argv[i], "router") == 0 || + Ustrcmp(argv[i], "transport") == 0 || + Ustrcmp(argv[i], "authenticator") == 0 || + Ustrcmp(argv[i], "macro") == 0 || + Ustrcmp(argv[i], "environment") == 0)) { - if (i < argc - 1 && - (Ustrcmp(argv[i], "router") == 0 || - Ustrcmp(argv[i], "transport") == 0 || - Ustrcmp(argv[i], "authenticator") == 0 || - Ustrcmp(argv[i], "macro") == 0 || - Ustrcmp(argv[i], "environment") == 0)) - { - readconf_print(argv[i+1], argv[i], flag_n); - i++; - } - else readconf_print(argv[i], NULL, flag_n); + fail |= !readconf_print(argv[i+1], argv[i], flag_n); + i++; } - exim_exit(EXIT_SUCCESS); + else + fail = !readconf_print(argv[i], NULL, flag_n); + } + exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS, US"main"); } if (list_config) { set_process_info("listing config"); - readconf_print(US"config", NULL, flag_n); - exim_exit(EXIT_SUCCESS); + exim_exit(readconf_print(US"config", NULL, flag_n) + ? EXIT_SUCCESS : EXIT_FAILURE, US"main"); } @@ -4641,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); + 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; @@ -4663,18 +4494,18 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) { fprintf(stderr, "failed to fork delivery process for %s: %s\n", argv[i], strerror(errno)); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } else wait(&status); } - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } /* 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 ", @@ -4686,7 +4517,7 @@ if (queue_interval == 0 && !daemon_listen) else set_process_info("running the queue (single queue run)"); queue_run(start_queue_run_id, stop_queue_run_id, FALSE); - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } @@ -4709,10 +4540,9 @@ for (i = 0;;) /* If user name has not been set by -F, set it from the passwd entry unless -f has been used to set the sender address by a trusted user. */ - if (originator_name == NULL) + if (!originator_name) { - if (sender_address == NULL || - (!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, '&'); @@ -4722,11 +4552,11 @@ for (i = 0;;) replaced by a copy of the login name, and some even specify that the first character should be upper cased, so that's what we do. */ - if (amp != NULL) + if (amp) { int loffset; string_format(buffer, sizeof(buffer), "%.*s%n%s%s", - amp - name, name, &loffset, originator_login, amp + 1); + (int)(amp - name), name, &loffset, originator_login, amp + 1); buffer[loffset] = toupper(buffer[loffset]); name = buffer; } @@ -4734,7 +4564,7 @@ for (i = 0;;) /* If a pattern for matching the gecos field was supplied, apply it and then expand the name string. */ - if (gecos_pattern != NULL && gecos_name != NULL) + if (gecos_pattern && gecos_name) { const pcre *re; re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */ @@ -4743,7 +4573,7 @@ for (i = 0;;) { uschar *new_name = expand_string(gecos_name); expand_nmax = -1; - if (new_name != NULL) + if (new_name) { DEBUG(D_receive) debug_printf("user name \"%s\" extracted from " "gecos field \"%s\"\n", new_name, name); @@ -4777,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) { @@ -4812,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) { @@ -4836,14 +4666,14 @@ 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"); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } rewrite_test(argv[test_rewrite_arg]); - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } /* A locally-supplied message is considered to be coming from a local user @@ -4851,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 @@ -4886,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 @@ -4912,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; @@ -4958,7 +4788,7 @@ if (verify_address_mode || address_test_mode) } route_tidyup(); - exim_exit(exit_value); + exim_exit(exit_value, US"main"); } /* Handle expansion checking. Either expand items on the command line, or read @@ -4972,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) @@ -4988,16 +4815,13 @@ if (expansion_test) /* Read a test message from a file. We fudge it up to be on stdin, saving stdin itself for later reading of expansion strings. */ - else if (expansion_test_message != NULL) + else if (expansion_test_message) { 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; @@ -5008,22 +4832,19 @@ if (expansion_test) clearerr(stdin); /* Required by Darwin */ } + /* Only admin users may see config-file macros this way */ + + 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 */ if (recipients_arg < argc) - { while (recipients_arg < argc) - { - uschar *s = argv[recipients_arg++]; - uschar *ss = expand_string(s); - if (ss == NULL) printf ("Failed: %s\n", expand_string_message); - else printf("%s\n", CS ss); - } - } + expansion_test_line(argv[recipients_arg++]); /* Read stdin */ @@ -5031,25 +4852,18 @@ if (expansion_test) { char *(*fn_readline)(const char *) = NULL; void (*fn_addhist)(const char *) = NULL; + uschar * s; - #ifdef USE_READLINE +#ifdef USE_READLINE void *dlhandle = set_readline(&fn_readline, &fn_addhist); - #endif +#endif - for (;;) - { - uschar *ss; - uschar *source = get_stdinput(fn_readline, fn_addhist); - if (source == NULL) break; - ss = expand_string(source); - if (ss == NULL) - printf ("Failed: %s\n", expand_string_message); - else printf("%s\n", CS ss); - } + while (s = get_stdinput(fn_readline, fn_addhist)) + expansion_test_line(s); - #ifdef USE_READLINE - if (dlhandle != NULL) dlclose(dlhandle); - #endif +#ifdef USE_READLINE + if (dlhandle) dlclose(dlhandle); +#endif } /* The data file will be open after -Mset */ @@ -5060,7 +4874,7 @@ if (expansion_test) deliver_datafile = -1; } - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main: expansion test"); } @@ -5074,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); @@ -5097,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); } @@ -5115,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" @@ -5154,7 +4968,7 @@ if (host_checking) } smtp_log_no_mail(); } - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } @@ -5167,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; } @@ -5198,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 */ @@ -5225,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 */ @@ -5240,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, @@ -5267,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); } @@ -5294,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. @@ -5318,7 +5131,7 @@ if (smtp_input) if (!smtp_start_session()) { mac_smtp_fflush(); - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"smtp_start toplevel"); } } @@ -5327,15 +5140,13 @@ if (smtp_input) else { thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); - if (expand_string_message != NULL) - { + if (expand_string_message) if (thismessage_size_limit == -1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand " "message_size_limit: %s", expand_string_message); else log_write(0, LOG_MAIN|LOG_PANIC_DIE, "invalid value for " "message_size_limit: %s", expand_string_message); - } } /* Loop for several messages when reading SMTP input. If we fork any child @@ -5366,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; @@ -5421,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,15 +5243,17 @@ while (more) more = receive_msg(extract_recipients); if (message_id[0] == 0) { + cancel_cutthrough_connection(TRUE, US"receive dropped"); if (more) goto moreloop; smtp_log_no_mail(); /* Log no mail if configured */ - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"receive toplevel"); } } else { + cancel_cutthrough_connection(TRUE, US"message setup dropped"); smtp_log_no_mail(); /* Log no mail if configured */ - exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE); + exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS, US"msg setup toplevel"); } } @@ -5459,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 */ @@ -5491,14 +5304,12 @@ while (more) if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: too many recipients\n"); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } else - { return moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; - } #ifdef SUPPORT_I18N { @@ -5515,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"; @@ -5527,7 +5338,7 @@ while (more) { fprintf(stderr, "exim: bad recipient address \"%s\": %s\n", string_printing(list[i]), errmess); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } else { @@ -5569,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, @@ -5582,7 +5393,7 @@ while (more) if (!receive_timeout) { - struct timeval t = { 30*60, 0 }; /* 30 minutes */ + struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 }; /* 30 minutes */ fd_set r; FD_ZERO(&r); FD_SET(0, &r); @@ -5600,7 +5411,7 @@ while (more) for real; when reading the headers of a message for filter testing, it is TRUE if the headers were terminated by '.' and FALSE otherwise. */ - if (message_id[0] == 0) exim_exit(EXIT_FAILURE); + if (message_id[0] == 0) exim_exit(EXIT_FAILURE, US"main"); } /* Non-SMTP message reception */ /* If this is a filter testing run, there are headers in store, but @@ -5645,7 +5456,7 @@ while (more) if (chdir("/")) /* Get away from wherever the user is running this from */ { DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n"); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, US"main"); } /* Now we run either a system filter test, or a user filter test, or both. @@ -5654,20 +5465,16 @@ while (more) explicitly. */ if ((filter_test & FTEST_SYSTEM) != 0) - { if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more)) - exim_exit(EXIT_FAILURE); - } + exim_exit(EXIT_FAILURE, US"main"); memcpy(filter_sn, filter_n, sizeof(filter_sn)); if ((filter_test & FTEST_USER) != 0) - { if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more)) - exim_exit(EXIT_FAILURE); - } + exim_exit(EXIT_FAILURE, US"main"); - exim_exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS, US"main"); } /* Else act on the result of message reception. We should not get here unless @@ -5708,27 +5515,34 @@ 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 connections). */ - if (local_queue_only) switch(queue_only_reason) + if (local_queue_only) { - case 2: - log_write(L_delay_delivery, - LOG_MAIN, "no immediate delivery: more than %d messages " - "received in one connection", smtp_accept_queue_per_connection); - break; + cancel_cutthrough_connection(TRUE, US"no delivery; queueing"); + switch(queue_only_reason) + { + case 2: + log_write(L_delay_delivery, + LOG_MAIN, "no immediate delivery: more than %d messages " + "received in one connection", smtp_accept_queue_per_connection); + break; - case 3: - log_write(L_delay_delivery, - LOG_MAIN, "no immediate delivery: load average %.2f", - (double)load_average/1000.0); - break; + case 3: + log_write(L_delay_delivery, + LOG_MAIN, "no immediate delivery: load average %.2f", + (double)load_average/1000.0); + break; + } } + 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 or froze the message. Always deliver in a separate process. A fork failure is not a disaster, as the delivery will eventually happen on a subsequent queue @@ -5737,7 +5551,7 @@ while (more) thereby defer the delivery if it tries to use (for example) a cached ldap connection that the parent has called unbind on. */ - else if (!queue_only_policy && !deliver_freeze) + else { pid_t pid; search_tidyup(); @@ -5753,8 +5567,7 @@ while (more) if (geteuid() != root_uid && !deliver_drop_privilege && !unprivileged) { - (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE, - 2, US"-Mc", message_id); + delivery_re_exec(CEE_EXEC_EXIT); /* Control does not return here. */ } @@ -5768,22 +5581,27 @@ while (more) if (pid < 0) { + cancel_cutthrough_connection(TRUE, US"delivery fork failed"); log_write(0, LOG_MAIN|LOG_PANIC, "failed to fork automatic delivery " "process: %s", strerror(errno)); } + else + { + release_cutthrough_connection(US"msg passed for delivery"); - /* In the parent, wait if synchronous delivery is required. This will - always be the case in MUA wrapper mode. */ + /* In the parent, wait if synchronous delivery is required. This will + always be the case in MUA wrapper mode. */ - else if (synchronous_delivery) - { - int status; - while (wait(&status) != pid); - if ((status & 0x00ff) != 0) - log_write(0, LOG_MAIN|LOG_PANIC, - "process %d crashed with signal %d while delivering %s", - (int)pid, status & 0x00ff, message_id); - if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE); + if (f.synchronous_delivery) + { + int status; + while (wait(&status) != pid); + if ((status & 0x00ff) != 0) + log_write(0, LOG_MAIN|LOG_PANIC, + "process %d crashed with signal %d while delivering %s", + (int)pid, status & 0x00ff, message_id); + if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"main"); + } } } @@ -5814,8 +5632,9 @@ moreloop: store_reset(reset_point); } -exim_exit(EXIT_SUCCESS); /* Never returns */ +exim_exit(EXIT_SUCCESS, US"main"); /* Never returns */ return 0; /* To stop compiler warning */ } + /* End of exim.c */