* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions for writing log files. The code for maintaining datestamped
log files was originally contributed by Tony Sheen. */
#include "exim.h"
-#define LOG_NAME_SIZE 256
#define MAX_SYSLOG_LEN 870
#define LOG_MODE_FILE 1
static uschar mainlog_name[LOG_NAME_SIZE];
static uschar rejectlog_name[LOG_NAME_SIZE];
-static uschar debuglog_name[LOG_NAME_SIZE];
static uschar *mainlog_datestamp = NULL;
static uschar *rejectlog_datestamp = NULL;
number definitions in macros.h */
static const uschar * exim_errstrings[] = {
- US"",
- US"unknown error",
- US"user slash",
- US"exist race",
- US"not regular",
- US"not directory",
- US"bad ugid",
- US"bad mode",
- US"inode changed",
- US"lock failed",
- US"bad address2",
- US"forbid pipe",
- US"forbid file",
- US"forbid reply",
- US"missing pipe",
- US"missing file",
- US"missing reply",
- US"bad redirect",
- US"smtp closed",
- US"smtp format",
- US"spool format",
- US"not absolute",
- US"Exim-imposed quota",
- US"held",
- US"Delivery filter process failure",
- US"Delivery add/remove header failure",
- US"Delivery write incomplete error",
- US"Some expansion failed",
- US"Failed to get gid",
- US"Failed to get uid",
- US"Unset or non-existent transport",
- US"MBX length mismatch",
- US"Lookup failed routing or in smtp tpt",
- US"Can't match format in appendfile",
- US"Creation outside home in appendfile",
- US"Can't check a list; lookup defer",
- US"DNS lookup defer",
- US"Failed to start TLS session",
- US"Mandatory TLS session not started",
- US"Failed to chown a file",
- US"Failed to create a pipe",
- US"When verifying",
- US"When required by client",
- US"Used internally in smtp transport",
- US"RCPT gave 4xx error",
- US"MAIL gave 4xx error",
- US"DATA gave 4xx error",
- US"Negotiation failed for proxy configured host",
- US"Authenticator 'other' failure",
- US"target not supporting SMTPUTF8",
- US"host is local",
- US"tainted filename",
-
- US"Not time for routing",
- US"Not time for local delivery",
- US"Not time for any remote host",
- US"Local-only delivery",
- US"Domain in queue_domains",
- US"Transport concurrency limit",
- US"Event requests alternate response",
+ [0] = US"",
+ [- ERRNO_UNKNOWNERROR] = US"unknown error",
+ [- ERRNO_USERSLASH] = US"user slash",
+ [- ERRNO_EXISTRACE] = US"exist race",
+ [- ERRNO_NOTREGULAR] = US"not regular",
+ [- ERRNO_NOTDIRECTORY] = US"not directory",
+ [- ERRNO_BADUGID] = US"bad ugid",
+ [- ERRNO_BADMODE] = US"bad mode",
+ [- ERRNO_INODECHANGED] = US"inode changed",
+ [- ERRNO_LOCKFAILED] = US"lock failed",
+ [- ERRNO_BADADDRESS2] = US"bad address2",
+ [- ERRNO_FORBIDPIPE] = US"forbid pipe",
+ [- ERRNO_FORBIDFILE] = US"forbid file",
+ [- ERRNO_FORBIDREPLY] = US"forbid reply",
+ [- ERRNO_MISSINGPIPE] = US"missing pipe",
+ [- ERRNO_MISSINGFILE] = US"missing file",
+ [- ERRNO_MISSINGREPLY] = US"missing reply",
+ [- ERRNO_BADREDIRECT] = US"bad redirect",
+ [- ERRNO_SMTPCLOSED] = US"smtp closed",
+ [- ERRNO_SMTPFORMAT] = US"smtp format",
+ [- ERRNO_SPOOLFORMAT] = US"spool format",
+ [- ERRNO_NOTABSOLUTE] = US"not absolute",
+ [- ERRNO_EXIMQUOTA] = US"Exim-imposed quota",
+ [- ERRNO_HELD] = US"held",
+ [- ERRNO_FILTER_FAIL] = US"Delivery filter process failure",
+ [- ERRNO_CHHEADER_FAIL] = US"Delivery add/remove header failure",
+ [- ERRNO_WRITEINCOMPLETE] = US"Delivery write incomplete error",
+ [- ERRNO_EXPANDFAIL] = US"Some expansion failed",
+ [- ERRNO_GIDFAIL] = US"Failed to get gid",
+ [- ERRNO_UIDFAIL] = US"Failed to get uid",
+ [- ERRNO_BADTRANSPORT] = US"Unset or non-existent transport",
+ [- ERRNO_MBXLENGTH] = US"MBX length mismatch",
+ [- ERRNO_UNKNOWNHOST] = US"Lookup failed routing or in smtp tpt",
+ [- ERRNO_FORMATUNKNOWN] = US"Can't match format in appendfile",
+ [- ERRNO_BADCREATE] = US"Creation outside home in appendfile",
+ [- ERRNO_LISTDEFER] = US"Can't check a list; lookup defer",
+ [- ERRNO_DNSDEFER] = US"DNS lookup defer",
+ [- ERRNO_TLSFAILURE] = US"Failed to start TLS session",
+ [- ERRNO_TLSREQUIRED] = US"Mandatory TLS session not started",
+ [- ERRNO_CHOWNFAIL] = US"Failed to chown a file",
+ [- ERRNO_PIPEFAIL] = US"Failed to create a pipe",
+ [- ERRNO_CALLOUTDEFER] = US"When verifying",
+ [- ERRNO_AUTHFAIL] = US"When required by client",
+ [- ERRNO_CONNECTTIMEOUT] = US"Used internally in smtp transport",
+ [- ERRNO_RCPT4XX] = US"RCPT gave 4xx error",
+ [- ERRNO_MAIL4XX] = US"MAIL gave 4xx error",
+ [- ERRNO_DATA4XX] = US"DATA gave 4xx error",
+ [- ERRNO_PROXYFAIL] = US"Negotiation failed for proxy configured host",
+ [- ERRNO_AUTHPROB] = US"Authenticator 'other' failure",
+ [- ERRNO_UTF8_FWD] = US"target not supporting SMTPUTF8",
+ [- ERRNO_HOST_IS_LOCAL] = US"host is local",
+ [- ERRNO_TAINT] = US"tainted filename",
+
+ [- ERRNO_RRETRY] = US"Not time for routing",
+
+ [- ERRNO_LRETRY] = US"Not time for local delivery",
+ [- ERRNO_HRETRY] = US"Not time for any remote host",
+ [- ERRNO_LOCAL_ONLY] = US"Local-only delivery",
+ [- ERRNO_QUEUE_DOMAIN] = US"Domain in queue_domains",
+ [- ERRNO_TRETRY] = US"Transport concurrency limit",
+
+ [- ERRNO_EVENT] = US"Event requests alternate response",
};
*/
static int
-log_open_already_exim(uschar * const name)
+log_open_already_exim(const uschar * const name)
{
int fd = -1;
const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
*/
int
-log_open_as_exim(uschar * const name)
+log_open_as_exim(const uschar * const name)
{
int fd = -1;
const uid_t euid = geteuid();
*/
static void
-open_log(int *fd, int type, uschar *tag)
+open_log(int * fd, int type, const uschar * tag)
{
uid_t euid;
BOOL ok, ok2;
default:
/* Remove any datestamp if this is the panic log. This is rare, so there's no
- need to optimize getting the datestamp length. We remove one non-alphanumeric
- char afterwards if at the start, otherwise one before. */
+ need to optimize getting the datestamp length. We remove one non-alphanumeric
+ char afterwards if at the start, otherwise one before. */
if (string_datestamp_offset >= 0)
{
uschar * from = buffer + string_datestamp_offset;
length actually written, persisting an errno from write()
*/
ssize_t
-write_to_fd_buf(int fd, const uschar *buf, size_t length)
+write_to_fd_buf(int fd, const uschar * buf, size_t length)
{
ssize_t wrote;
size_t total_written = 0;
-const uschar *p = buf;
+const uschar * p = buf;
size_t left = length;
while (1)
return total_written;
}
+static inline ssize_t
+write_gstring_to_fd_buf(int fd, const gstring * g)
+{
+return write_to_fd_buf(fd, g->s, g->ptr);
+}
+
static void
{
int paniclogfd;
ssize_t written_len;
-gstring gs = { .size = LOG_BUFFER_SIZE-1, .ptr = 0, .s = log_buffer };
+gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer };
gstring * g;
va_list ap;
}
va_end(ap);
- g->size = LOG_BUFFER_SIZE;
- g = string_catn(g, US"\n", 1);
- debug_printf("%s", string_from_gstring(g));
+ debug_printf("%Y\n", g);
- gs.size = LOG_BUFFER_SIZE-1; /* Having used the buffer for debug output, */
+ gs.size = LOG_BUFFER_SIZE-2; /* Having used the buffer for debug output, */
gs.ptr = 0; /* reset it for the real use. */
gs.s = log_buffer;
}
if (!syslog_pid) pid_position[1] = g->ptr; /* … and end+1 of the PID */
}
-if (f.really_exim && message_id[0] != 0)
+if (f.really_exim && message_id[0])
g = string_fmt_append(g, "%s ", message_id);
if (flags & LOG_CONFIG)
&& g->ptr < LOG_BUFFER_SIZE - 6
&& raw_recipients_count > 0)
{
- int i;
g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL);
- for (i = 0; i < raw_recipients_count; i++)
+ for (int i = 0; i < raw_recipients_count; i++)
{
- uschar * s = raw_recipients[i];
+ const uschar * s = raw_recipients[i];
if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break;
g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s);
}
}
+/* actual size, now we are placing the newline (and space for NUL) */
+gs.size = LOG_BUFFER_SIZE;
g = string_catn(g, US"\n", 1);
string_from_gstring(g);
/* Failing to write to the log is disastrous */
- written_len = write_to_fd_buf(mainlogfd, g->s, g->ptr);
+ written_len = write_gstring_to_fd_buf(mainlogfd, g);
if (written_len != g->ptr)
{
log_write_failed(US"main log", g->ptr, written_len);
g = g2;
else /* Buffer is full; truncate */
{
- g->ptr -= 100; /* For message and separator */
- if (g->s[g->ptr-1] == '\n') g->ptr--;
+ gstring_trim(g, 100); /* For message and separator */
+ gstring_trim_trailing(g, '\n');
g = string_cat(g, US"\n*** truncated ***\n");
break;
}
if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
}
- written_len = write_to_fd_buf(rejectlogfd, g->s, g->ptr);
+ written_len = write_gstring_to_fd_buf(rejectlogfd, g);
if (written_len != g->ptr)
{
log_write_failed(US"reject log", g->ptr, written_len);
if (panic_save_buffer)
(void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
- written_len = write_to_fd_buf(paniclogfd, g->s, g->ptr);
+ written_len = write_gstring_to_fd_buf(paniclogfd, g);
if (written_len != g->ptr)
{
int save_errno = errno;
/* Give up if the DIE flag is set */
if ((flags & LOG_PANIC_DIE) != LOG_PANIC)
- die(NULL, US"Unexpected failure, please try later");
+ if (panic_coredump)
+ kill(getpid(), SIGSEGV); /* deliberate trap */
+ else
+ die(NULL, US"Unexpected failure, please try later");
}
}
*/
void
-decode_bits(unsigned int *selector, size_t selsize, int *notall,
- uschar *string, bit_table *options, int count, uschar *which, int flags)
+decode_bits(unsigned int * selector, size_t selsize, int * notall,
+ const uschar * string, bit_table * options, int count, uschar * which,
+ int flags)
{
uschar *errmsg;
if (!string) return;
{
char *end; /* Not uschar */
memset(selector, 0, sizeof(*selector)*selsize);
- *selector = strtoul(CS string+1, &end, 0);
+ *selector = strtoul(CCS string+1, &end, 0);
if (!*end) return;
errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
string);
else for(;;)
{
BOOL adding;
- uschar *s;
+ const uschar * s;
int len;
- bit_table *start, *end;
+ bit_table * start, * end;
Uskip_whitespace(&string);
if (!*string) return;
to pass the name through child_exxec_exim(). */
void
-debug_logging_activate(uschar *tag_name, uschar *opts)
+debug_logging_activate(const uschar * tag_name, const uschar * opts)
{
if (debug_file)
{
}
+void
+debug_logging_from_spool(const uschar * filename)
+{
+if (debug_fd < 0)
+ {
+ Ustrncpy(debuglog_name, filename, sizeof(debuglog_name));
+ if ((debug_fd = log_open_as_exim(filename)) >= 0)
+ debug_file = fdopen(debug_fd, "w");
+ DEBUG(D_deliver) debug_printf("debug enabled by spoolfile\n");
+ }
+/*
+else DEBUG(D_deliver)
+ debug_printf("debug already active; ignoring spoolfile '%s'\n", filename);
+*/
+}
+
+
void
debug_logging_stop(BOOL kill)
{
+debug_printf("debug terminated by %s\n", kill ? "kill" : "stop");
debug_pretrigger_discard();
if (!debug_file || !debuglog_name[0]) return;