Cleanup: when smtp_generic_maps is turned on, don't parse
MIME structures in the message body. Victor Duchovni. File:
smtp/smtp_proto.c.
+
+20061210
+
+ Robustness: low-cost re-entrancy guard that allows daemons
+ to call msg_fatal() etc. from a signal handler, without
+ risking memory corruption, or deadlock on Redhat Linux.
+ This works provided that the signal handler never returns.
+ In that special case we need not guarantee after-the-fact
+ consistency of the interrupted process. File: util/msg_output.c.
+
+ Robustness: replace exit() calls by _exit(). File: util/msg.c,
+ bounce/bounce_cleanup.c.
+
+ Cleanup: document under what conditions these protections
+ work, with REENTRANCY sections in the relevant man pages.
+ Files: util/vbuf.c. util/msg.c, util/msg_output.c.
+
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.
- For further assistance, please send mail to <postmaster>
+ For further assistance, please send mail to postmaster.
If you do so, please include this problem report. You can
delete your own text from the attached returned message.
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.
-For further assistance, please send mail to <postmaster>
+For further assistance, please send mail to postmaster.
If you do so, please include this problem report. You can
delete your own text from the attached returned message.
# I'm sorry to have to inform you that your message could not
# be delivered to one or more recipients. It's attached below.
#
-# For further assistance, please send mail to <postmaster>
+# For further assistance, please send mail to postmaster.
#
# If you do so, please include this problem report. You can
# delete your own text from the attached returned message.
*/
if (bounce_cleanup_path)
(void) unlink(vstring_str(bounce_cleanup_path));
- exit(sig);
+ _exit(sig);
}
/* bounce_cleanup_register - register logfile to clean up */
"I'm sorry to have to inform you that your message could not",
"be delivered to one or more recipients. It's attached below.",
"",
- "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER,
+ "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER ".",
"",
"If you do so, please include this problem report. You can",
"delete your own text from the attached returned message.",
,
"It will be retried until it is $maximal_queue_lifetime_days day(s) old.",
"",
- "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER,
+ "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER ".",
"",
"If you do so, please include this problem report. You can",
"delete your own text from the attached returned message.",
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20061208"
-#define MAIL_VERSION_NUMBER "2.3.5-RC2"
+#define MAIL_RELEASE_DATE "20061211"
+#define MAIL_VERSION_NUMBER "2.3.5"
#ifdef SNAPSHOT
# define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
/* to the standard error stream, but the disposition can be changed
/* by the user. See the hints below in the SEE ALSO section.
/*
-/* msg_info(), msg_warn(), msg_error(), msg_fatal() and msg_panic()
+/* msg_info(), msg_warn(), msg_error(), msg_fatal*() and msg_panic()
/* produce a one-line record with the program name, a severity code
/* (except for msg_info()), and an informative message. The program
/* name must have been set by calling one of the msg_XXX_init()
/* msg_verbose is a global flag that can be set to make software
/* more verbose about what it is doing. By default the flag is zero.
/* By convention, a larger value means more noise.
+/* REENTRANCY
+/* .ad
+/* .fi
+/* The msg_info() etc. output routines are protected against
+/* ordinary recursive calls and against re-entry by signal
+/* handlers.
+/*
+/* Protection against re-entry by signal handlers is subject
+/* to the following limitations:
+/* .IP \(bu
+/* The signal handlers must never return. In other words, the
+/* signal handlers must do one or more of the following: call
+/* _exit(), kill the process with a signal, and permanently block
+/* the process.
+/* .IP \(bu
+/* The signal handlers must invoke msg_info() etc. not until
+/* after the msg_XXX_init() functions complete initialization,
+/* and not until after the first formatted output to a VSTRING
+/* or VSTREAM.
+/* .IP \(bu
+/* Each msg_cleanup() call-back function, and each Postfix or
+/* system function invoked by that call-back function, either
+/* protects itself against recursive calls and re-entry by a
+/* terminating signal handler, or is called exclusively by the
+/* msg(3) module.
+/* .PP
+/* When re-entrancy is detected, the requested output and
+/* optional cleanup operations are skipped. Skipping the output
+/* operations prevents memory corruption of VSTREAM_ERR data
+/* structures, and prevents deadlock on Linux releases that
+/* use mutexes within system library routines such as syslog().
+/* This protection exists under the condition that these
+/* specific resources are accessed exclusively via the msg_info()
+/* etc. functions.
/* SEE ALSO
/* msg_output(3) specify diagnostics disposition
/* msg_stdio(3) direct diagnostics to standard I/O stream
int msg_verbose = 0;
/*
- * Private state. The msg_exiting flag prevents us from recursively
- * reporting an error.
+ * Private state.
*/
static MSG_CLEANUP_FN msg_cleanup_fn = 0;
-static int msg_exiting = 0;
static int msg_error_count = 0;
static int msg_error_bound = 13;
+ /*
+ * The msg_exiting flag prevents us from recursively reporting an error with
+ * msg_fatal*() or msg_panic(), and provides a first-level safety net for
+ * optional cleanup actions against signal handler re-entry problems. Note
+ * that msg_vprintf() implements its own guard against re-entry.
+ *
+ * XXX We specify global scope, to discourage the compiler from doing smart
+ * things.
+ */
+volatile int msg_exiting = 0;
+
/* msg_info - report informative message */
void msg_info(const char *fmt,...)
msg_cleanup_fn();
}
sleep(1);
- exit(1);
+ /* In case we're running as a signal handler. */
+ _exit(1);
}
/* msg_fatal_status - report error and terminate gracefully */
msg_cleanup_fn();
}
sleep(1);
- exit(status);
+ /* In case we're running as a signal handler. */
+ _exit(status);
}
/* msg_panic - report error and dump core */
}
sleep(1);
abort(); /* Die! */
- exit(1); /* DIE!! */
+ /* In case we're running as a signal handler. */
+ _exit(1); /* DIE!! */
}
/* msg_cleanup - specify cleanup routine */
/*
/* msg_text() copies a pre-formatted text, sanitizes the result, and
/* calls the output handlers registered with msg_output().
+/* REENTRANCY
+/* .ad
+/* .fi
+/* The above output routines are protected against ordinary
+/* recursive calls and against re-entry by signal
+/* handlers, with the following limitations:
+/* .IP \(bu
+/* The signal handlers must never return. In other words, the
+/* signal handlers must do one or more of the following: call
+/* _exit(), kill the process with a signal, and permanently
+/* block the process.
+/* .IP \(bu
+/* The signal handlers must call the above output routines not
+/* until after msg_output() completes initialization, and not
+/* until after the first formatted output to a VSTRING or
+/* VSTREAM.
+/* .IP \(bu
+/* Each msg_output() call-back function, and each Postfix or
+/* system function called by that call-back function, either
+/* must protect itself against recursive calls and re-entry
+/* by a terminating signal handler, or it must be called
+/* exclusively by functions in the msg_output(3) module.
+/* .PP
+/* When re-entrancy is detected, the requested output operation
+/* is skipped. This prevents memory corruption of VSTREAM_ERR
+/* data structures, and prevents deadlock on Linux releases
+/* that use mutexes within system library routines such as
+/* syslog(). This protection exists under the condition that
+/* these specific resources are accessed exclusively via
+/* msg_output() call-back functions.
/* LICENSE
/* .ad
/* .fi
#include <percentm.h>
#include <msg_output.h>
+ /*
+ * Global scope, to discourage the compiler from doing smart things.
+ */
+volatile int msg_vprintf_lock;
+volatile int msg_text_lock;
+
/*
* Private state.
*/
void msg_output(MSG_OUTPUT_FN output_fn)
{
+ /*
+ * Allocate all resources during initialization.
+ */
+ if (msg_buffer == 0)
+ msg_buffer = vstring_alloc(100);
+
/*
* We're not doing this often, so avoid complexity and allocate memory
* for an exact fit.
void msg_vprintf(int level, const char *format, va_list ap)
{
- if (msg_buffer == 0)
- msg_buffer = vstring_alloc(100);
- vstring_vsprintf(msg_buffer, percentm(format, errno), ap);
- msg_text(level, vstring_str(msg_buffer));
+ if (msg_vprintf_lock == 0) {
+ msg_vprintf_lock = 1;
+ /* OK if terminating signal handler hijacks control before next stmt. */
+ vstring_vsprintf(msg_buffer, percentm(format, errno), ap);
+ msg_text(level, vstring_str(msg_buffer));
+ msg_vprintf_lock = 0;
+ }
}
/* msg_text - sanitize and log pre-formatted text */
/*
* Sanitize the text. Use a private copy if necessary.
*/
- if (msg_buffer == 0)
- msg_buffer = vstring_alloc(100);
- if (text != vstring_str(msg_buffer))
- vstring_strcpy(msg_buffer, text);
- printable(vstring_str(msg_buffer), '?');
- if (msg_output_fn_count == 0)
- msg_vstream_init("unknown", VSTREAM_ERR);
- for (i = 0; i < msg_output_fn_count; i++)
- msg_output_fn[i] (level, vstring_str(msg_buffer));
+ if (msg_text_lock == 0) {
+ msg_text_lock = 1;
+ /* OK if terminating signal handler hijacks control before next stmt. */
+ if (text != vstring_str(msg_buffer))
+ vstring_strcpy(msg_buffer, text);
+ printable(vstring_str(msg_buffer), '?');
+ /* On-the-fly initialization for debugging test programs only. */
+ if (msg_output_fn_count == 0)
+ msg_vstream_init("unknown", VSTREAM_ERR);
+ for (i = 0; i < msg_output_fn_count; i++)
+ msg_output_fn[i] (level, vstring_str(msg_buffer));
+ msg_text_lock = 0;
+ }
}
/* In addition, vbuf_print() recognizes the %m format specifier
/* and expands it to the error message corresponding to the current
/* value of the global \fIerrno\fR variable.
+/* REENTRANCY
+/* .ad
+/* .fi
+/* vbuf_print() allocates a static buffer. After completion
+/* of the first vbuf_print() call, this buffer is safe for
+/* reentrant vbuf_print() calls by (asynchronous) terminating
+/* signal handlers or by (synchronous) terminating error
+/* handlers. vbuf_print() initialization typically happens
+/* upon the first formatted output to a VSTRING or VSTREAM.
+/*
+/* However, it is up to the caller to ensure that the destination
+/* VSTREAM or VSTRING buffer is protected against reentrant usage.
/* LICENSE
/* .ad
/* .fi