built-in or service-defined parameters for ldap, *sql, etc.
database names. Problem reported by Christian Rößner. Files:
postconf/postconf_user.c.
+
+20180306
+
+ Bugfix (introduced: 19990302): when luser_relay specifies
+ a non-existent local address, the luser_relay feature becomes
+ a black hole. Reported by Jørgen Thomsen. File: local/unknown.c.
+
+20180422
+
+ Bugfix (introduced: Postfix 2.8): missing tls_server_start()
+ error propagation in tlsproxy(8) resulting in segfault after
+ TLS handshake error. Found during code maintenance. File:
+ tlsproxy/tlsproxy.c.
+
+20180509
+
+ Bugfix (introduced: 20170617): postconf(1) command segfault
+ if unable to open a Postfix database configuration file due
+ to a file permission error. Report by Andreas Hasenack, fix
+ by Viktor Dukhovni. File: postconf/postconf_dbms.c.
+
+20180519
+
+ Cleanup: Postfix did not support running as a PID=1 process,
+ which complicated Postfix management in containers. The
+ "postfix start-fg" command will now run the Postfix master
+ daemon as a PID=1 process if possible. Thanks to inputs
+ from Andreas Schulze, Eray Aslan, and Viktor Dukhovni.
+ Files: postfix/postfix.c, master/master.c, master/master.h,
+ master/master_sig.c, conf/postfix-script.
;;
start-fg)
# Foreground start-up is incompatible with multi-instance mode.
- # We can't use "exec $daemon_directory/master" here: that would
- # break process group management, and "postfix stop" would kill
- # too many processes.
+ # Use "exec $daemon_directory/master" only if PID == 1.
+ # Otherwise, doing so would break process group management,
+ # and "postfix stop" would kill too many processes.
case $instances in
- "") $daemon_directory/master
+ "") case $$ in
+ 1) exec $daemon_directory/master -i
+ $FATAL "cannot start-fg the master daemon"
+ exit 1;;
+ *) $daemon_directory/master;;
+ esac
;;
*) $FATAL "start-fg does not support multi_instance_directories"
exit 1
master - Postfix master process
<b>SYNOPSIS</b>
- <b>master</b> [<b>-Ddtvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-e</b> <i>exit</i><b>_</b><i>time</i>]
+ <b>master</b> [<b>-Dditvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-e</b> <i>exit</i><b>_</b><i>time</i>]
<b>DESCRIPTION</b>
The <a href="master.8.html"><b>master</b>(8)</a> daemon is the resident process that runs Postfix daemons
Terminate the master process after <i>exit</i><b>_</b><i>time</i> seconds. Child pro-
cesses terminate at their convenience.
+ <b>-i</b> Enable <b>init</b> mode: do not attempt to become a session or process
+ group leader; and to force termination, set an explicit signal
+ handler instead of relying on the default signal action. This
+ mode is allowed only if the process ID equals 1.
+
<b>-t</b> Test mode. Return a zero exit status when the <b>master.pid</b> lock
file does not exist or when that file is not locked. This is
evidence that the <a href="master.8.html"><b>master</b>(8)</a> daemon is not running.
check described above.
<b>start-fg</b>
- Like <b>start</b>, but keep the master daemon running in the fore-
- ground. This requires that multi-instance support is disabled
- (i.e. the <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> parameter value must be
- empty). When running Postfix inside a container, mount the con-
- tainer host's /dev/log socket inside the container (example:
- "docker run -v /dev/log:/dev/log ...") and specify a distinct
- Postfix "<a href="postconf.5.html#syslog_name">syslog_name</a>" prefix that identifies logging from the
+ Like <b>start</b>, but keep the <a href="master.8.html"><b>master</b>(8)</a> daemon running in the fore-
+ ground, and enable <a href="master.8.html"><b>master</b>(8)</a> "init" mode when running as PID 1.
+ This command requires that multi-instance support is disabled
+ (i.e. the <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> parameter value must be
+ empty). When running Postfix inside a container, mount the con-
+ tainer host's /dev/log socket inside the container (example:
+ "docker run -v /dev/log:/dev/log ...") and specify a distinct
+ Postfix "<a href="postconf.5.html#syslog_name">syslog_name</a>" prefix that identifies logging from the
Postfix instance.
<b>stop</b> Stop the Postfix mail system in an orderly fashion. If possible,
Start the Postfix mail system. This also runs the configuration
check described above.
.IP \fBstart\-fg\fR
-Like \fBstart\fR, but keep the master daemon running in the
-foreground. This requires that multi\-instance support is
+Like \fBstart\fR, but keep the \fBmaster\fR(8) daemon running
+in the foreground, and enable \fBmaster\fR(8) "init" mode
+when running as PID 1.
+This command requires that multi\-instance support is
disabled (i.e. the multi_instance_directories parameter
value must be empty). When running Postfix inside a container,
mount the container host's /dev/log socket inside the
.SH "SYNOPSIS"
.na
.nf
-\fBmaster\fR [\fB\-Ddtvw\fR] [\fB\-c \fIconfig_dir\fR] [\fB\-e \fIexit_time\fR]
+\fBmaster\fR [\fB\-Dditvw\fR] [\fB\-c \fIconfig_dir\fR] [\fB\-e \fIexit_time\fR]
.SH DESCRIPTION
.ad
.fi
.IP "\fB\-e \fIexit_time\fR"
Terminate the master process after \fIexit_time\fR seconds. Child
processes terminate at their convenience.
+.IP \fB\-i\fR
+Enable \fBinit\fR mode: do not attempt to become a session
+or process group leader; and to force termination, set an
+explicit signal handler instead of relying on the default
+signal action. This mode is allowed only if the process ID
+equals 1.
.IP \fB\-t\fR
Test mode. Return a zero exit status when the \fBmaster.pid\fR lock
file does not exist or when that file is not locked. This is evidence
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20180221"
-#define MAIL_VERSION_NUMBER "3.3.0"
+#define MAIL_RELEASE_DATE "20180519"
+#define MAIL_VERSION_NUMBER "3.3.1"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
#include <sent.h>
#include <deliver_pass.h>
#include <defer.h>
+#include <canon_addr.h>
/* Application-specific. */
#include "local.h"
+#define STREQ(x,y) (strcasecmp((x),(y)) == 0)
+
/* deliver_unknown - delivery for unknown recipients */
int deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
const char *myname = "deliver_unknown";
int status;
VSTRING *expand_luser;
+ VSTRING *canon_luser;
static MAPS *transp_maps;
const char *map_transport;
if (*var_luser_relay) {
state.msg_attr.unmatched = 0;
expand_luser = vstring_alloc(100);
+ canon_luser = vstring_alloc(100);
local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (void *) 0);
- status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
+ /* In case luser_relay specifies a domain-less address. */
+ canon_addr_external(canon_luser, vstring_str(expand_luser));
+ /* Assumes that the address resolver won't change the address. */
+ if (STREQ(vstring_str(canon_luser), state.msg_attr.rcpt.address)) {
+ dsb_simple(state.msg_attr.why, "5.1.1",
+ "unknown user: \"%s\"", state.msg_attr.user);
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+ status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
+ }
+ vstring_free(canon_luser);
vstring_free(expand_luser);
return (status);
}
* If no alias was found for a required reserved name, toss the message
* into the bit bucket, and issue a warning instead.
*/
-#define STREQ(x,y) (strcasecmp(x,y) == 0)
-
if (STREQ(state.msg_attr.user, MAIL_ADDR_MAIL_DAEMON)
|| STREQ(state.msg_attr.user, MAIL_ADDR_POSTMASTER)) {
msg_warn("required alias not found: %s", state.msg_attr.user);
/* SUMMARY
/* Postfix master process
/* SYNOPSIS
-/* \fBmaster\fR [\fB-Ddtvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-e \fIexit_time\fR]
+/* \fBmaster\fR [\fB-Dditvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-e \fIexit_time\fR]
/* DESCRIPTION
/* The \fBmaster\fR(8) daemon is the resident process that runs Postfix
/* daemons on demand: daemons to send or receive messages via the
/* .IP "\fB-e \fIexit_time\fR"
/* Terminate the master process after \fIexit_time\fR seconds. Child
/* processes terminate at their convenience.
+/* .IP \fB-i\fR
+/* Enable \fBinit\fR mode: do not attempt to become a session
+/* or process group leader; and to force termination, set an
+/* explicit signal handler instead of relying on the default
+/* signal action. This mode is allowed only if the process ID
+/* equals 1.
/* .IP \fB-t\fR
/* Test mode. Return a zero exit status when the \fBmaster.pid\fR lock
/* file does not exist or when that file is not locked. This is evidence
#include "master.h"
int master_detach = 1;
+int init_mode = 0;
/* master_exit_event - exit for memory leak testing purposes */
/*
* Process JCL.
*/
- while ((ch = GETOPT(argc, argv, "c:Dde:tvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "c:Dde:itvw")) > 0) {
switch (ch) {
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
case 'e':
event_request_timer(master_exit_event, (void *) 0, atoi(optarg));
break;
+ case 'i':
+ if (getpid() != 1)
+ msg_fatal("-i is allowed only for PID 1 process");
+ init_mode = 1;
+ break;
case 'D':
debug_me = 1;
break;
*/
if (test_lock && wait_flag)
msg_fatal("the -t and -w options cannot be used together");
+ if (init_mode && (debug_me || !master_detach || wait_flag))
+ msg_fatal("the -i option cannot be used with -D, -d, or -w");
/*
* Run a foreground monitor process that returns an exit status of 0 when
* all MTA processes cleanly. Give up if we can't separate from our
* parent process. We're not supposed to blow away the parent.
*/
- if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid())
+ if (init_mode == 0 && debug_me == 0 && master_detach != 0
+ && setsid() == -1 && getsid(0) != getpid())
msg_fatal("unable to set session and process group ID: %m");
/*
* master.c
*/
extern int master_detach;
+extern int init_mode;
/*
* master_ent.c
*/
msg_info("terminating on signal %d", sig);
+ /*
+ * Undocumented: when a process runs with PID 1, Linux won't deliver a
+ * signal unless the process specifies a handler (i.e. SIG_DFL is treated
+ * as SIG_IGN).
+ */
+ if (init_mode)
+ /* Don't call exit() from a signal handler. */
+ _exit(0);
+
/*
* Deliver the signal to ourselves and clean up. XXX We're running as a
* signal handler and really should not be doing complicated things...
*/
dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0);
dict_register(dict_spec, dict);
- if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0
- && errno != EACCES) {
- msg_warn("open \"%s\" configuration \"%s\": %m",
- dp->db_type, cf_file);
+ if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0) {
+ if (errno != EACCES)
+ msg_warn("open \"%s\" configuration \"%s\": %m",
+ dp->db_type, cf_file);
myfree(dict_spec);
return;
}
/* Start the Postfix mail system. This also runs the configuration
/* check described above.
/* .IP \fBstart-fg\fR
-/* Like \fBstart\fR, but keep the master daemon running in the
-/* foreground. This requires that multi-instance support is
+/* Like \fBstart\fR, but keep the \fBmaster\fR(8) daemon running
+/* in the foreground, and enable \fBmaster\fR(8) "init" mode
+/* when running as PID 1.
+/* This command requires that multi-instance support is
/* disabled (i.e. the multi_instance_directories parameter
/* value must be empty). When running Postfix inside a container,
/* mount the container host's /dev/log socket inside the
/* tlsp_start_tls - turn on TLS or force disconnect */
-static void tlsp_start_tls(TLSP_STATE *state)
+static int tlsp_start_tls(TLSP_STATE *state)
{
TLS_SERVER_START_PROPS props;
static char *cipher_grade;
if (state->tls_context == 0) {
tlsp_state_free(state);
- return;
+ return (-1);
}
/*
* XXX Do we care about certificate verification results? Not as long as
* postscreen(8) doesn't actually receive email.
*/
+ return (0);
}
/* tlsp_get_fd_event - receive final postscreen(8) hand-off information */
* Perform the TLS layer before-handshake initialization. We perform the
* remainder after the TLS handshake completes.
*/
- tlsp_start_tls(state);
+ if (tlsp_start_tls(state) < 0)
+ return;
/*
* Trigger the initial proxy server I/Os.
* Schedule an ALARM signal, and make sure the signal will be delivered
* even if we are being called from a signal handler and SIGALRM delivery
* is blocked.
+ *
+ * Undocumented: when a process runs with PID 1, Linux won't deliver a
+ * signal unless the process specifies a handler (i.e. SIG_DFL is treated
+ * as SIG_IGN). Conveniently, _exit() can be used directly as a signal
+ * handler. This changes the wait status that a parent would see, but in
+ * the case of "init" mode on Linux, no-one would care.
*/
alarm(0);
sigemptyset(&sig_action.sa_mask);
sig_action.sa_flags = 0;
- sig_action.sa_handler = SIG_DFL;
+ sig_action.sa_handler = (getpid() == 1 ? _exit : SIG_DFL);
sigaction(SIGALRM, &sig_action, (struct sigaction *) 0);
alarm(seconds);
sigaddset(&sig_action.sa_mask, SIGALRM);