X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/789f8a4f4046120b7ae2aafa45f7f45c3ae4c8f5..829dd84217ed9c32fda88a4ca2cb20b41c950f1e:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 4ea42fdc2..5e11559ad 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -12,6 +12,10 @@ Also a few functions that don't naturally fit elsewhere. */ #include "exim.h" +#ifdef __GLIBC__ +# include +#endif + #ifdef USE_GNUTLS # include # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP) @@ -170,10 +174,8 @@ Returns: nothing void set_process_info(const char *format, ...) { -int len; +int len = sprintf(CS process_info, "%5d ", (int)getpid()); va_list ap; -sprintf(CS process_info, "%5d ", (int)getpid()); -len = Ustrlen(process_info); va_start(ap, format); if (!string_vformat(process_info + len, PROCESS_INFO_SIZE - len - 2, format, ap)) Ustrcpy(process_info + len, "**** string overflowed buffer ****"); @@ -363,7 +365,7 @@ return 0; /* Exim uses a time + a pid to generate a unique identifier in two places: its message IDs, and in file names for maildir deliveries. Because some OS now re-use pids within the same second, sub-second times are now being used. -However, for absolute certaintly, we must ensure the clock has ticked before +However, for absolute certainty, we must ensure the clock has ticked before allowing the relevant process to complete. At the time of implementation of this code (February 2003), the speed of processors is such that the clock will invariably have ticked already by the time a process has done its job. This @@ -838,6 +840,15 @@ fprintf(f, "Support for:"); #ifdef SUPPORT_SOCKS fprintf(f, " SOCKS"); #endif +#ifdef TCP_FASTOPEN + fprintf(f, " TCP_Fast_Open"); +#endif +#ifdef EXPERIMENTAL_LMDB + fprintf(f, " Experimental_LMDB"); +#endif +#ifdef EXPERIMENTAL_QUEUEFILE + fprintf(f, " Experimental_QUEUEFILE"); +#endif #ifdef EXPERIMENTAL_SPF fprintf(f, " Experimental_SPF"); #endif @@ -883,6 +894,9 @@ fprintf(f, "Lookups (built-in):"); #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 fprintf(f, " ldap ldapdn ldapm"); #endif +#ifdef EXPERIMENTAL_LMDB + fprintf(f, " lmdb"); +#endif #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 fprintf(f, " mysql"); #endif @@ -988,6 +1002,9 @@ fprintf(f, "Transports:"); #ifdef TRANSPORT_PIPE fprintf(f, " pipe"); #endif +#ifdef EXPERIMENTAL_QUEUEFILE + fprintf(f, " queuefile"); +#endif #ifdef TRANSPORT_SMTP fprintf(f, " smtp"); #endif @@ -1002,6 +1019,8 @@ if (fixed_never_users[0] > 0) fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]); } +fprintf(f, "Configure owner: %d:%d\n", config_uid, config_gid); + fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); /* Everything else is details which are only worth reporting when debugging. @@ -1025,6 +1044,14 @@ DEBUG(D_any) do { fprintf(f, "Compiler: \n"); #endif +#ifdef __GLIBC__ + fprintf(f, "Library version: Glibc: Compile: %d.%d\n", + __GLIBC__, __GLIBC_MINOR__); + if (__GLIBC_PREREQ(2, 1)) + fprintf(f, " Runtime: %s\n", + gnu_get_libc_version()); +#endif + #ifdef SUPPORT_TLS tls_version_report(f); #endif @@ -1040,7 +1067,7 @@ DEBUG(D_any) do { characters; unless it's an ancient version of PCRE in which case it is not defined. */ #ifndef PCRE_PRERELEASE -#define PCRE_PRERELEASE +# define PCRE_PRERELEASE #endif #define QUOTE(X) #X #define EXPAND_AND_QUOTE(X) QUOTE(X) @@ -1304,7 +1331,7 @@ static void exim_usage(uschar *progname) { -/* Handle specific program invocation varients */ +/* Handle specific program invocation variants */ if (Ustrcmp(progname, US"-mailq") == 0) { fprintf(stderr, @@ -1331,12 +1358,12 @@ exit(EXIT_FAILURE); /* Typically, Exim will drop privileges if macros are supplied. In some cases, we want to not do so. -Arguments: none (macros is a global) +Arguments: opt_D_used - true if the commandline had a "-D" option Returns: true if trusted, false otherwise */ static BOOL -macros_trusted(void) +macros_trusted(BOOL opt_D_used) { #ifdef WHITELIST_D_MACROS macro_item *m; @@ -1346,7 +1373,7 @@ size_t len; BOOL prev_char_item, found; #endif -if (macros == NULL) +if (!opt_D_used) return TRUE; #ifndef WHITELIST_D_MACROS return FALSE; @@ -1403,8 +1430,9 @@ for (p = whitelisted, i = 0; (p != end) && (i < white_count); ++p) } whites[i] = NULL; -/* The list of macros should be very short. Accept the N*M complexity. */ -for (m = macros; m != NULL; m = m->next) +/* 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) { found = FALSE; for (w = whites; *w; ++w) @@ -1494,6 +1522,7 @@ BOOL list_config = FALSE; BOOL local_queue_only; BOOL more = TRUE; BOOL one_msg_action = FALSE; +BOOL opt_D_used = FALSE; BOOL queue_only_set = FALSE; BOOL receiving_message = TRUE; BOOL sender_ident_set = FALSE; @@ -1635,8 +1664,7 @@ os_non_restarting_signal(SIGALRM, sigalrm_handler); /* Ensure we have a buffer for constructing log entries. Use malloc directly, because store_malloc writes a log entry on failure. */ -log_buffer = (uschar *)malloc(LOG_BUFFER_SIZE); -if (log_buffer == NULL) +if (!(log_buffer = US malloc(LOG_BUFFER_SIZE))) { fprintf(stderr, "exim: failed to get store for log buffer\n"); exit(EXIT_FAILURE); @@ -1872,7 +1900,7 @@ for (i = 1; i < argc; i++) break; } - /* An option consistion of -- terminates the options */ + /* An option consisting of -- terminates the options */ if (Ustrcmp(arg, "--") == 0) { @@ -2047,6 +2075,7 @@ for (i = 1; i < argc; i++) sender_host_address = argv[i]; host_checking = checking = log_testing_mode = TRUE; host_checking_callout = argrest[1] == 'c'; + message_logs = FALSE; } /* -bi: This option is used by sendmail to initialize *the* alias file, @@ -2393,11 +2422,11 @@ for (i = 1; i < argc; i++) #else { int ptr = 0; - macro_item *mlast = NULL; macro_item *m; uschar name[24]; uschar *s = argrest; + opt_D_used = TRUE; while (isspace(*s)) s++; if (*s < 'A' || *s > 'Z') @@ -2421,22 +2450,14 @@ for (i = 1; i < argc; i++) while (isspace(*s)) s++; } - for (m = macros; m != NULL; m = m->next) - { + for (m = macros; m; m = m->next) if (Ustrcmp(m->name, name) == 0) { fprintf(stderr, "exim: duplicated -D in command line\n"); exit(EXIT_FAILURE); } - mlast = m; - } - m = store_get(sizeof(macro_item) + Ustrlen(name)); - m->next = NULL; - m->command_line = TRUE; - if (mlast == NULL) macros = m; else mlast->next = m; - Ustrcpy(m->name, name); - m->replacement = string_copy(s); + m = macro_create(name, s, TRUE, FALSE); if (clmacro_count >= MAX_CLMACROS) { @@ -2704,66 +2725,63 @@ for (i = 1; i < argc; i++) break; } + else if (*argrest == 'C' && argrest[1] && !argrest[2]) + { + switch(argrest[1]) + { /* -MCA: set the smtp_authenticated flag; this is useful only when it precedes -MC (see above). The flag indicates that the host to which Exim is connected has accepted an AUTH sequence. */ - else if (Ustrcmp(argrest, "CA") == 0) - { - smtp_authenticated = TRUE; - break; - } + case 'A': 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 */ - else if (Ustrcmp(argrest, "CD") == 0) - { - smtp_use_dsn = TRUE; - break; - } + + case 'D': smtp_peer_options |= PEER_OFFERED_DSN; break; + + /* -MCG: set the queue name, to a non-default value */ + + case 'G': if (++i < argc) queue_name = string_copy(argv[i]); + else badarg = TRUE; + break; + + /* -MCK: the peer offered CHUNKING. Must precede -MC */ + + case 'K': smtp_peer_options |= PEER_OFFERED_CHUNKING; break; /* -MCP: set the smtp_use_pipelining flag; this is useful only when it preceded -MC (see above) */ - else if (Ustrcmp(argrest, "CP") == 0) - { - smtp_use_pipelining = TRUE; - break; - } + case 'P': smtp_peer_options |= PEER_OFFERED_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 is useful only when it precedes -MC (see above) */ - else if (Ustrcmp(argrest, "CQ") == 0) - { - if(++i < argc) passed_qr_pid = (pid_t)(Uatol(argv[i])); - else badarg = TRUE; - if(++i < argc) passed_qr_pipe = (int)(Uatol(argv[i])); - else badarg = TRUE; - break; - } + case 'Q': if (++i < argc) passed_qr_pid = (pid_t)(Uatol(argv[i])); + else badarg = TRUE; + if (++i < argc) passed_qr_pipe = (int)(Uatol(argv[i])); + else badarg = TRUE; + break; /* -MCS: set the smtp_use_size flag; this is useful only when it precedes -MC (see above) */ - else if (Ustrcmp(argrest, "CS") == 0) - { - smtp_use_size = TRUE; - break; - } + case 'S': smtp_peer_options |= PEER_OFFERED_SIZE; break; +#ifdef SUPPORT_TLS /* -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. */ - #ifdef SUPPORT_TLS - else if (Ustrcmp(argrest, "CT") == 0) - { - tls_offered = TRUE; - break; + case 'T': smtp_peer_options |= PEER_OFFERED_TLS; break; +#endif + + default: badarg = TRUE; break; + } + break; } - #endif /* -M[x]: various operations on the following list of message ids: -M deliver the messages, ignoring next retry times and thawing @@ -3214,7 +3232,7 @@ for (i = 1; i < argc; i++) if (*argrest == 'f') { queue_run_force = TRUE; - if (*(++argrest) == 'f') + if (*++argrest == 'f') { deliver_force_thaw = TRUE; argrest++; @@ -3229,8 +3247,19 @@ for (i = 1; i < argc; i++) argrest++; } - /* -q[f][f][l]: Run the queue, optionally forced, optionally local only, - optionally starting from a given message id. */ + /* -q[f][f][l][G]... Work on the named queue */ + + if (*argrest == 'G') + { + int i; + for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++; + queue_name = string_copyn(argrest, i); + argrest += i; + if (*argrest == '/') argrest++; + } + + /* -q[f][f][l][G]: Run the queue, optionally forced, optionally local + only, optionally named, optionally starting from a given message id. */ if (*argrest == 0 && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) @@ -3242,20 +3271,14 @@ for (i = 1; i < argc; i++) stop_queue_run_id = argv[++i]; } - /* -q[f][f][l]: Run the queue at regular intervals, optionally forced, - optionally local only. */ + /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally + forced, optionally local only, optionally named. */ - else + else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i], + 0, FALSE)) <= 0) { - if (*argrest != 0) - queue_interval = readconf_readtime(argrest, 0, FALSE); - else - queue_interval = readconf_readtime(argv[++i], 0, FALSE); - if (queue_interval <= 0) - { - fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); - exit(EXIT_FAILURE); - } + fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); + exit(EXIT_FAILURE); } break; @@ -3275,8 +3298,7 @@ for (i = 1; i < argc; i++) if (*argrest != 0) { int i; - for (i = 0; i < sizeof(rsopts)/sizeof(uschar *); i++) - { + for (i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { if (i != 2) queue_run_force = TRUE; @@ -3284,21 +3306,20 @@ for (i = 1; i < argc; i++) if (i == 1 || i == 4) deliver_force_thaw = TRUE; argrest += Ustrlen(rsopts[i]); } - } } /* -R: Set string to match in addresses for forced queue run to pick out particular messages. */ - if (*argrest == 0) + if (*argrest) + deliver_selectstring = argrest; + else if (i+1 < argc) + deliver_selectstring = argv[++i]; + else { - if (i+1 < argc) deliver_selectstring = argv[++i]; else - { - fprintf(stderr, "exim: string expected after -R\n"); - exit(EXIT_FAILURE); - } + fprintf(stderr, "exim: string expected after -R\n"); + exit(EXIT_FAILURE); } - else deliver_selectstring = argrest; break; @@ -3319,11 +3340,10 @@ for (i = 1; i < argc; i++) in all cases provided there are no further characters in this argument. */ - if (*argrest != 0) + if (*argrest) { int i; - for (i = 0; i < sizeof(rsopts)/sizeof(uschar *); i++) - { + for (i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { if (i != 2) queue_run_force = TRUE; @@ -3331,21 +3351,20 @@ for (i = 1; i < argc; i++) if (i == 1 || i == 4) deliver_force_thaw = TRUE; argrest += Ustrlen(rsopts[i]); } - } } /* -S: Set string to match in addresses for forced queue run to pick out particular messages. */ - if (*argrest == 0) + if (*argrest) + deliver_selectstring_sender = argrest; + else if (i+1 < argc) + deliver_selectstring_sender = argv[++i]; + else { - if (i+1 < argc) deliver_selectstring_sender = argv[++i]; else - { - fprintf(stderr, "exim: string expected after -S\n"); - exit(EXIT_FAILURE); - } + fprintf(stderr, "exim: string expected after -S\n"); + exit(EXIT_FAILURE); } - else deliver_selectstring_sender = argrest; break; /* -Tqt is an option that is exclusively for use by the testing suite. @@ -3459,8 +3478,9 @@ for (i = 1; i < argc; i++) /* If -R or -S have been specified without -q, assume a single queue run. */ -if ((deliver_selectstring != NULL || deliver_selectstring_sender != NULL) && - queue_interval < 0) queue_interval = 0; +if ( (deliver_selectstring || deliver_selectstring_sender) + && queue_interval < 0) + queue_interval = 0; END_ARG: @@ -3476,12 +3496,12 @@ if (( ) || ( msg_action_arg > 0 && - (daemon_listen || queue_interval >= 0 || list_options || + (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) && + (daemon_listen || queue_interval > 0) && (sender_address != NULL || list_options || list_queue || checking || bi_option) ) || @@ -3668,7 +3688,7 @@ configuration file changes and macro definitions haven't happened. */ if (( /* EITHER */ (!trusted_config || /* Config changed, or */ - !macros_trusted()) && /* impermissible macros and */ + !macros_trusted(opt_D_used)) && /* impermissible macros and */ real_uid != root_uid && /* Not root, and */ !running_in_test_harness /* Not fudged */ ) || /* OR */ @@ -3920,7 +3940,6 @@ if (Ustrlen(syslog_processname) > 32) "syslog_processname is longer than 32 chars: aborting"); if (log_oneline) - { if (admin_user) { log_write(0, LOG_MAIN, "%s", log_oneline); @@ -3928,7 +3947,6 @@ if (log_oneline) } else return EXIT_FAILURE; - } /* In some operating systems, the environment variable TMPDIR controls where temporary files are created; Exim doesn't use these (apart from when delivering @@ -3942,17 +3960,14 @@ EXIM_TMPDIR by the build scripts. #ifdef EXIM_TMPDIR { uschar **p; - if (environ) for (p = USS environ; *p != NULL; p++) - { - if (Ustrncmp(*p, "TMPDIR=", 7) == 0 && - Ustrcmp(*p+7, EXIM_TMPDIR) != 0) + if (environ) for (p = USS environ; *p; p++) + if (Ustrncmp(*p, "TMPDIR=", 7) == 0 && Ustrcmp(*p+7, EXIM_TMPDIR) != 0) { - uschar *newp = malloc(Ustrlen(EXIM_TMPDIR) + 8); + uschar * newp = store_malloc(Ustrlen(EXIM_TMPDIR) + 8); sprintf(CS newp, "TMPDIR=%s", EXIM_TMPDIR); *p = newp; DEBUG(D_any) debug_printf("reset TMPDIR=%s in environment\n", EXIM_TMPDIR); } - } } #endif @@ -3966,33 +3981,28 @@ about this earlier - but hopefully nothing will normally be logged earlier than 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 != NULL && strcmpic(timezone_string, US"UTC") == 0) - { +if (timezone_string && strcmpic(timezone_string, US"UTC") == 0) timestamps_utc = TRUE; - } else { uschar *envtz = US getenv("TZ"); - if ((envtz == NULL && timezone_string != NULL) || - (envtz != NULL && - (timezone_string == NULL || - Ustrcmp(timezone_string, envtz) != 0))) + if (envtz + ? !timezone_string || Ustrcmp(timezone_string, envtz) != 0 + : timezone_string != NULL + ) { uschar **p = USS environ; uschar **new; uschar **newp; int count = 0; - if (environ) while (*p++ != NULL) count++; - if (envtz == NULL) count++; - newp = new = malloc(sizeof(uschar *) * (count + 1)); - if (environ) for (p = USS environ; *p != NULL; p++) - { - if (Ustrncmp(*p, "TZ=", 3) == 0) continue; - *newp++ = *p; - } - if (timezone_string != NULL) + if (environ) while (*p++) count++; + if (!envtz) count++; + newp = new = store_malloc(sizeof(uschar *) * (count + 1)); + if (environ) for (p = USS environ; *p; p++) + if (Ustrncmp(*p, "TZ=", 3) != 0) *newp++ = *p; + if (timezone_string) { - *newp = malloc(Ustrlen(timezone_string) + 4); + *newp = store_malloc(Ustrlen(timezone_string) + 4); sprintf(CS *newp++, "TZ=%s", timezone_string); } *newp = NULL; @@ -4024,16 +4034,15 @@ Exim user", but it hasn't, because either the -D option set macros, or the root for -C or -D, the caller must either be root or be invoking a trusted configuration file (when deliver_drop_privilege is false). */ -if (removed_privilege && (!trusted_config || macros != NULL) && - real_uid == exim_uid) - { +if ( removed_privilege + && (!trusted_config || opt_D_used) + && real_uid == exim_uid) if (deliver_drop_privilege) 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"); - } /* Start up Perl interpreter if Perl support is configured and there is a perl_startup option, and the configuration or the command line specifies @@ -4657,7 +4666,10 @@ if (queue_interval == 0 && !daemon_listen) (start_queue_run_id == NULL)? US"" : start_queue_run_id, (stop_queue_run_id == NULL)? US"" : US" stopping at ", (stop_queue_run_id == NULL)? US"" : stop_queue_run_id); - set_process_info("running the queue (single queue run)"); + if (*queue_name) + set_process_info("running the '%s' queue (single queue run)", queue_name); + 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); } @@ -5075,7 +5087,7 @@ if (host_checking) verify_get_ident(1413); } - /* In case the given address is a non-canonical IPv6 address, canonicize + /* In case the given address is a non-canonical IPv6 address, canonicalize it. The code works for both IPv4 and IPv6, as it happens. */ size = host_aton(sender_host_address, x); @@ -5324,7 +5336,7 @@ February 2003: That's *still* not the end of the story. There are now versions of Linux (where SIG_IGN does work) that are picky. If, having set SIG_IGN, a process then calls waitpid(), a grumble is written to the system log, because this is logically inconsistent. In other words, it doesn't like the paranoia. -As a consequenc of this, the waitpid() below is now excluded if we are sure +As a consequence of this, the waitpid() below is now excluded if we are sure that SIG_IGN works. */ if (!synchronous_delivery) @@ -5528,7 +5540,7 @@ while (more) ignored; rejecting here would just add complication, and it can just as well be done later. Allow $recipients to be visible in the ACL. */ - if (acl_not_smtp_start != NULL) + if (acl_not_smtp_start) { uschar *user_msg, *log_msg; enable_dollar_recipients = TRUE; @@ -5537,6 +5549,20 @@ while (more) enable_dollar_recipients = FALSE; } + /* Pause for a while waiting for input. If none received in that time, + close the logfile, if we had one open; then if we wait for a long-running + datasource (months, in one use-case) log rotation will not leave us holding + the file copy. */ + + if (!receive_timeout) + { + struct timeval t = { 30*60, 0 }; /* 30 minutes */ + fd_set r; + + FD_ZERO(&r); FD_SET(0, &r); + if (select(1, &r, NULL, NULL, &t) == 0) mainlog_close(); + } + /* Read the data for the message. If filter_test is not FTEST_NONE, this will just read the headers for the message, and not write anything onto the spool. */ @@ -5701,8 +5727,8 @@ 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); + (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE, + 2, US"-Mc", message_id); /* Control does not return here. */ }