util/msg_output.h, util/unix_dgram_connect.c,
util/unix_dgram_listen.c.
-20180124
-
Safety: temporary postlogd fix to avoid recursion when main.cf
has "maillog_file =" but master(8) still still tells its child
processes to send logs to postlogd. File: postlogd/postlogd.c.
+
+20190126
+
+ Safety: Postfix programs will log to either syslog or postlog
+ but not both; and postlogd forwards postlog logging to
+ syslog, when a configuration change removes the maillog_file
+ pathname, but some programs still use the old configuration.
+ Files: util/msg_syslog.[hc], util/msg_logger.c,
+ global/maillog_client.c, postlogd/postlogd.c,
+
+ Bugfix (introduced: Postfix 1.0): watchdog pipe file
+ descriptor leak. This pipe provides one source of liveness,
+ data from this pipe is discarded, and therefore this does
+ not enable privilege escalation or DOS. File: util/watchdog.c.
+
+ Feature: stdout logging support; requires "postfix start-fg"
+ and "maillog_file = /dev/stdout". Files: master/master.c,
+ conf/postfix-script.
the software under the license of their choice. Those who are more
comfortable with the IPL can continue with that license.
+Major changes with snapshot 20190126-nonprod
+============================================
+
+Support for logging to stdout. This requires Postfix startup with
+"postfix start-fg" and a main.cf file with "maillog_file = /dev/stdout".
+It does not get easier than this.
+
Major changes with snapshot 20190121-nonprod
============================================
1) exec $daemon_directory/master -i
$FATAL "cannot start-fg the master daemon"
exit 1;;
- *) $daemon_directory/master;;
+ *) $daemon_directory/master -s;;
esac
;;
*) $FATAL "start-fg does not support multi_instance_directories"
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>-i</b> Enable <b>init</b> mode: do not become a session or process group
+ leader; similar to <b>-s</b>, do not redirect stdout to /dev/null, so
+ that "<a href="postconf.5.html#maillog_file">maillog_file</a> = /dev/stdout" works. 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
+ <b>-s</b> Do not redirect stdout to /dev/null, so that "<a href="postconf.5.html#maillog_file">maillog_file</a> =
+ /dev/stdout" works.
+
+ <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.
- <b>-v</b> Enable verbose logging for debugging purposes. This option is
+ <b>-v</b> Enable verbose logging for debugging purposes. This option is
passed on to child processes. Multiple <b>-v</b> options make the soft-
ware increasingly verbose.
<b>-w</b> Wait in a dummy foreground process, while the real master daemon
- initializes in a background process. The dummy foreground
- process returns a zero exit status only if the master daemon
- initialization is successful, and if it completes in a reason-
+ initializes in a background process. The dummy foreground
+ process returns a zero exit status only if the master daemon
+ initialization is successful, and if it completes in a reason-
able amount of time.
This feature is available in Postfix 2.10 and later.
Signals:
<b>SIGHUP</b> Upon receipt of a <b>HUP</b> signal (e.g., after "<b>postfix reload</b>"), the
- master process re-reads its configuration files. If a service
- has been removed from the <a href="master.5.html"><b>master.cf</b></a> file, its running processes
- are terminated immediately. Otherwise, running processes are
- allowed to terminate as soon as is convenient, so that changes
+ master process re-reads its configuration files. If a service
+ has been removed from the <a href="master.5.html"><b>master.cf</b></a> file, its running processes
+ are terminated immediately. Otherwise, running processes are
+ allowed to terminate as soon as is convenient, so that changes
in configuration settings affect only new service requests.
<b>SIGTERM</b>
Upon receipt of a <b>TERM</b> signal (e.g., after "<b>postfix abort</b>"), the
- master process passes the signal on to its child processes and
- terminates. This is useful for an emergency shutdown. Normally
- one would terminate only the master ("<b>postfix stop</b>") and allow
+ master process passes the signal on to its child processes and
+ terminates. This is useful for an emergency shutdown. Normally
+ one would terminate only the master ("<b>postfix stop</b>") and allow
running processes to finish what they are doing.
<b>DIAGNOSTICS</b>
- Problems are reported to <b>syslogd</b>(8). The exit status is non-zero in
- case of problems, including problems while initializing as a master
+ Problems are reported to <b>syslogd</b>(8). The exit status is non-zero in
+ case of problems, including problems while initializing as a master
daemon process in the background.
<b>ENVIRONMENT</b>
<b>MAIL_DEBUG</b>
- After initialization, start a debugger as specified with the
+ After initialization, start a debugger as specified with the
<b><a href="postconf.5.html#debugger_command">debugger_command</a></b> configuration parameter in the <a href="postconf.5.html"><b>main.cf</b></a> configu-
ration file.
Directory with Postfix configuration files.
<b>CONFIGURATION PARAMETERS</b>
- Unlike most Postfix daemon processes, the <a href="master.8.html"><b>master</b>(8)</a> server does not
- automatically pick up changes to <a href="postconf.5.html"><b>main.cf</b></a>. Changes to <a href="master.5.html"><b>master.cf</b></a> are
- never picked up automatically. Use the "<b>postfix reload</b>" command after
+ Unlike most Postfix daemon processes, the <a href="master.8.html"><b>master</b>(8)</a> server does not
+ automatically pick up changes to <a href="postconf.5.html"><b>main.cf</b></a>. Changes to <a href="master.5.html"><b>master.cf</b></a> are
+ never picked up automatically. Use the "<b>postfix reload</b>" command after
a configuration change.
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#default_process_limit">default_process_limit</a> (100)</b>
- The default maximal number of Postfix child processes that pro-
+ The default maximal number of Postfix child processes that pro-
vide a given service.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix daemon process
+ The maximum amount of time that an idle Postfix daemon process
waits for an incoming connection before terminating voluntarily.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
process will service before terminating voluntarily.
<b><a href="postconf.5.html#service_throttle_time">service_throttle_time</a> (60s)</b>
- How long the Postfix <a href="master.8.html"><b>master</b>(8)</a> waits before forking a server
+ How long the Postfix <a href="master.8.html"><b>master</b>(8)</a> waits before forking a server
that appears to be malfunctioning.
Available in Postfix version 2.6 and later:
<b><a href="postconf.5.html#master_service_disable">master_service_disable</a> (empty)</b>
- Selectively disable <a href="master.8.html"><b>master</b>(8)</a> listener ports by service type or
+ Selectively disable <a href="master.8.html"><b>master</b>(8)</a> listener ports by service type or
by service name and type.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_directory">daemon_directory</a> (see 'postconf -d' output)</b>
invoked with the -D option.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
- The network interface addresses that this mail system receives
+ The network interface addresses that this mail system receives
mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
- The Internet protocols Postfix will attempt to use when making
+ The Internet protocols Postfix will attempt to use when making
or accepting connections.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#mail_owner">mail_owner</a> (postfix)</b>
- The UNIX system account that owns the Postfix queue and most
+ The UNIX system account that owns the Postfix queue and most
Postfix daemon processes.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix 3.3 and later:
The <a href="master.5.html">master.cf</a> service name of a Postfix daemon process.
<b>FILES</b>
- To expand the directory names below into their actual values, use the
+ To expand the directory names below into their actual values, use the
command "<b>postconf <a href="postconf.5.html#config_directory">config_directory</a></b>" etc.
$<a href="postconf.5.html#config_directory">config_directory</a>/<a href="postconf.5.html">main.cf</a>, global configuration file.
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.
+Enable \fBinit\fR mode: do not become a session or process
+group leader; similar to \fB\-s\fR, do not redirect stdout
+to /dev/null, so that "maillog_file = /dev/stdout" works.
+This mode is allowed only if the process ID equals 1.
+.IP \fB\-s\fR
+Do not redirect stdout to /dev/null, so that "maillog_file
+= /dev/stdout" works.
.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
*/
get_mail_conf_str_table(first_str_defaults);
- if (!msg_syslog_facility(var_syslog_facility))
+ if (!msg_syslog_set_facility(var_syslog_facility))
msg_fatal("file %s/%s: parameter %s: unrecognized value: %s",
var_config_dir, MAIN_CONF_FILE,
VAR_SYSLOG_FACILITY, var_syslog_facility);
/* are easier to recognize. The mail system name is specified
/* with the "syslog_name" configuration parameter.
/*
-/* The result is volatile. Make a copy of the result if it is
-/* to be used for any appreciable amount of time.
+/* The result overwritten with each call.
+/*
+/* A null argv0 argument requests that the current
+/* result is returned.
/* LICENSE
/* .ad
/* .fi
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
const char *slash;
const char *tag;
- if (canon_name == 0)
- canon_name = vstring_alloc(10);
- if ((slash = strrchr(argv0, '/')) != 0 && slash[1])
- argv0 = slash + 1;
- /* Setenv()-ed from main.cf, or inherited from master. */
- if ((tag = safe_getenv(CONF_ENV_LOGTAG)) == 0)
- /* Check main.cf settings directly, in case set-gid. */
- tag = var_syslog_name ? var_syslog_name :
- mail_conf_eval(DEF_SYSLOG_NAME);
- vstring_sprintf(canon_name, "%s/%s", tag, argv0);
+ if (argv0) {
+ if (canon_name == 0)
+ canon_name = vstring_alloc(10);
+ if ((slash = strrchr(argv0, '/')) != 0 && slash[1])
+ argv0 = slash + 1;
+ /* Setenv()-ed from main.cf, or inherited from master. */
+ if ((tag = safe_getenv(CONF_ENV_LOGTAG)) == 0)
+ /* Check main.cf settings directly, in case set-gid. */
+ tag = var_syslog_name ? var_syslog_name :
+ mail_conf_eval(DEF_SYSLOG_NAME);
+ vstring_sprintf(canon_name, "%s/%s", tag, argv0);
+ }
return (vstring_str(canon_name));
}
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20190124"
+#define MAIL_RELEASE_DATE "20190126"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
/* NAME
/* maillog_client 3
/* SUMMARY
-/* syslog client or internal log client
+/* choose between syslog client and postlog client
/* SYNOPSIS
/* #include <maillog_client.h>
/*
/* const char *progname,
/* int flags)
/* DESCRIPTION
-/* maillog_client_init() chooses between logging to syslog or
-/* to the internal postlog service, based on the value of the
-/* "maillog_file" parameter setting and postlog-related
-/* environment settings.
+/* maillog_client_init() chooses between logging to the syslog
+/* service or to the internal postlog service.
/*
-/* This code may be called before a process has initialized
-/* its configuration parameters. A daemon process will receive
-/* logging hints from its parent, through environment variables.
-/* In all cases, a process may invoke maillog_client_init()
-/* any time, for example, after it initializes or updates its
-/* configuration parameters.
+/* maillog_client_init() may be called before configuration
+/* parameters are initialized. During this time, logging is
+/* controlled by the presence or absence of POSTLOG_SERVICE
+/* in the process environment (this is ignored if a program
+/* runs with set-uid or set-gid permissions).
+/*
+/* maillog_client_init() may also be called after configuration
+/* parameters are initialized. During this time, logging is
+/* controlled by the "maillog_file" parameter value.
/*
/* Arguments:
/* .IP progname
/* .ad
/* .fi
/* .IP "maillog_file (empty)"
-/* The name of an optional logfile. If the value is empty, and
-/* the environment does not specify POSTLOG_SERVICE, the program
-/* will log to the syslog service instead.
+/* The name of an optional logfile. If the value is empty, or
+/* unitialized and the process environment does not specify
+/* POSTLOG_SERVICE, the program will log to the syslog service
+/* instead.
/* .IP "myhostname (default: see postconf -d output)"
/* The internet hostname of this mail system.
/* .IP "postlog_service_name (postlog)"
/* The name of the internal postlog logging service.
/* SEE ALSO
-/* msg_syslog(3) syslog client msg_logger(3) internal
-/* logger
+/* msg_syslog(3) syslog client
+/* msg_logger(3) internal logger
/* LICENSE
/* .ad
/* .fi
* Application-specific.
*/
static int maillog_client_flags;
-static int using_syslog;
#define POSTLOG_SERVICE_ENV "POSTLOG_SERVICE"
#define POSTLOG_HOSTNAME_ENV "POSTLOG_HOSTNAME"
{
static int fallback_guard = 0;
- if (fallback_guard == 0
+ /*
+ * Guard against recursive calls.
+ *
+ * If an error happened before the maillog_file parameter was initialized,
+ * or if maillog_file logging is disabled, then we cannot fall back to a
+ * logfile. All we can do is to hope that stderr logging will bring out
+ * the bad news.
+ */
+ if (fallback_guard == 0 && var_maillog_file && *var_maillog_file
&& logwriter_one_shot(var_maillog_file, text, strlen(text)) < 0) {
fallback_guard = 1;
- if (maillog_client_flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK)
- msg_fatal("logfile '%s' is not available: %m", var_maillog_file);
+ msg_fatal("logfile '%s' is not available: %m", var_maillog_file);
}
}
void maillog_client_init(const char *progname, int flags)
{
- char *maillog_file;
char *import_service_path;
char *import_hostname;
- char *service_path;
- char *myhostname;
+
+ /*
+ * Crucially, only one logger mode can be in effect at any time,
+ * otherwise postlogd(8) may go into a loop.
+ */
+ enum {
+ MAILLOG_CLIENT_MODE_SYSLOG, MAILLOG_CLIENT_MODE_POSTLOG,
+ } logger_mode;
/*
* Security: this code may run before the import_environment setting has
* taken effect. It has to guard against privilege escalation attacks on
* setgid programs, using malicious environment settings.
*
- * Import the postlog service name and hostname from the environment, if the
- * process has not yet processed main.cf and command-line options.
+ * Import the postlog service name and hostname from the environment.
+ *
+ * - These will be used and kept if the process has not yet initialized its
+ * configuration parameters.
+ *
+ * - These will be set or updated if the configuration enables postlog
+ * logging.
+ *
+ * - These will be removed if the configuration does not enable postlog
+ * logging.
*/
if ((import_service_path = safe_getenv(POSTLOG_SERVICE_ENV)) != 0
&& *import_service_path == 0)
import_service_path = 0;
- maillog_file = (var_maillog_file && *var_maillog_file ?
- var_maillog_file : 0);
+ if ((import_hostname = safe_getenv(POSTLOG_HOSTNAME_ENV)) != 0
+ && *import_hostname == 0)
+ import_hostname = 0;
#if MAILLOG_CLIENT_DEBUG
#define STRING_OR_NULL(s) ((s) ? (s) : "(null)")
msg_syslog_init(progname, LOG_PID, LOG_FACILITY);
msg_info("import_service_path=%s", STRING_OR_NULL(import_service_path));
- msg_info("maillog_file=%s", STRING_OR_NULL(maillog_file));
+ msg_info("import_hostname=%s", STRING_OR_NULL(import_hostname));
#endif
/*
- * Logging to syslog. Either internal logging is disabled, or this is a
- * non-daemon program that does not yet know its configuration parameter
- * values.
+ * Before configuration parameters are initialized, the logging mode is
+ * controlled by the presence or absence of POSTLOG_SERVICE in the
+ * process environment. After configuration parameters are initialized,
+ * the logging mode is controlled by the "maillog_file" parameter value.
+ *
+ * The configured mode may change after a process is started. The
+ * postlogd(8) server will proxy logging to syslogd where needed.
*/
- if (import_service_path == 0 && maillog_file == 0) {
- msg_syslog_init(progname, LOG_PID, LOG_FACILITY);
- using_syslog = 1;
- return;
+ if (var_maillog_file ? *var_maillog_file == 0 : import_service_path == 0) {
+ logger_mode = MAILLOG_CLIENT_MODE_SYSLOG;
+ } else {
+ /* var_maillog_file ? *var_maillog_file : import_service_path != 0 */
+ logger_mode = MAILLOG_CLIENT_MODE_POSTLOG;
}
/*
- * If we enabled syslog with the above code during an earlier call, then
- * update the 'progname' as that may have changed. However, it may be
- * 'better' to silence or unregister the msg_syslog handler.
+ * Postlog logging is enabled. Update the 'progname' as that may have
+ * changed since an earlier call, and update the environment settings if
+ * they differ from configuration settings. This blends two code paths,
+ * one code path where configuration parameters are initialized (the
+ * preferred path), and one code path that uses imports from environment.
*/
- if (using_syslog)
- msg_syslog_init(progname, LOG_PID, LOG_FACILITY);
+ if (logger_mode == MAILLOG_CLIENT_MODE_POSTLOG) {
+ char *myhostname;
+ char *service_path;
- /*
- * Logging to postlog (or to postlog fallback file).
- */
- if ((import_hostname = safe_getenv(POSTLOG_HOSTNAME_ENV)) != 0
- && *import_hostname == 0)
- import_hostname = 0;
- if (var_myhostname && *var_myhostname) {
- myhostname = var_myhostname;
- } else if ((myhostname = import_hostname) == 0) {
- myhostname = "amnesiac";
- }
+ if (var_myhostname && *var_myhostname) {
+ myhostname = var_myhostname;
+ } else if ((myhostname = import_hostname) == 0) {
+ myhostname = "amnesiac";
+ }
#if MAILLOG_CLIENT_DEBUG
- msg_info("import_hostname=%s", STRING_OR_NULL(import_hostname));
- msg_info("myhostname=%s", STRING_OR_NULL(myhostname));
+ msg_info("myhostname=%s", STRING_OR_NULL(myhostname));
#endif
- if (var_postlog_service) {
- service_path = concatenate(var_queue_dir, "/", MAIL_CLASS_PUBLIC,
- "/", var_postlog_service, (char *) 0);
- } else {
- service_path = import_service_path;
- }
- maillog_client_flags = flags;
- msg_logger_init(progname, myhostname, service_path,
- ((flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK)
- && maillog_file) ?
- maillog_client_logwriter_fallback :
- (MSG_LOGGER_FALLBACK_FN) 0);
+ if (var_postlog_service) {
+ service_path = concatenate(var_queue_dir, "/", MAIL_CLASS_PUBLIC,
+ "/", var_postlog_service, (char *) 0);
+ } else {
+ service_path = import_service_path;
+ }
+ maillog_client_flags = flags;
+ msg_logger_init(progname, myhostname, service_path,
+ (flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK) ?
+ maillog_client_logwriter_fallback :
+ (MSG_LOGGER_FALLBACK_FN) 0);
- /*
- * After processing main.cf, export the postlog service pathname and the
- * hostname, so that a child process can log to postlogd before it has
- * processed main.cf and command-line options.
- */
- if (import_service_path == 0
- || strcmp(service_path, import_service_path) != 0) {
+ /*
+ * Export or update the exported postlog service pathname and the
+ * hostname, so that a child process can bootstrap postlog logging
+ * before it has processed main.cf and command-line options.
+ */
+ if (import_service_path == 0
+ || strcmp(service_path, import_service_path) != 0) {
#if MAILLOG_CLIENT_DEBUG
- msg_info("export %s=%s", POSTLOG_SERVICE_ENV, service_path);
+ msg_info("export %s=%s", POSTLOG_SERVICE_ENV, service_path);
#endif
- if (setenv(POSTLOG_SERVICE_ENV, service_path, 1) < 0)
- msg_fatal("setenv: %m");
- }
- if (import_hostname == 0 || strcmp(myhostname, import_hostname) != 0) {
+ if (setenv(POSTLOG_SERVICE_ENV, service_path, 1) < 0)
+ msg_fatal("setenv: %m");
+ }
+ if (import_hostname == 0 || strcmp(myhostname, import_hostname) != 0) {
#if MAILLOG_CLIENT_DEBUG
- msg_info("export %s=%s", POSTLOG_HOSTNAME_ENV, myhostname);
+ msg_info("export %s=%s", POSTLOG_HOSTNAME_ENV, myhostname);
#endif
- if (setenv(POSTLOG_HOSTNAME_ENV, myhostname, 1) < 0)
- msg_fatal("setenv: %m");
+ if (setenv(POSTLOG_HOSTNAME_ENV, myhostname, 1) < 0)
+ msg_fatal("setenv: %m");
+ }
+ if (service_path != import_service_path)
+ myfree(service_path);
+ }
+
+ /*
+ * Postlog logging is disabled. Silence the msg_logger client, and remove
+ * the environment settings that bootstrap postlog logging in a child
+ * process.
+ */
+ else {
+ msg_logger_control(CA_MSG_LOGGER_CTL_DISABLE, CA_MSG_LOGGER_CTL_END);
+ if ((import_service_path && unsetenv(POSTLOG_SERVICE_ENV))
+ || (import_hostname && unsetenv(POSTLOG_HOSTNAME_ENV)))
+ msg_fatal("unsetenv: %m");
+ }
+
+ /*
+ * Syslog logging is enabled. Update the 'progname' as that may have
+ * changed since an earlier call.
+ */
+ if (logger_mode == MAILLOG_CLIENT_MODE_SYSLOG) {
+ msg_syslog_init(progname, LOG_PID, LOG_FACILITY);
+ }
+
+ /*
+ * Syslog logging is disabled, silence the syslog client.
+ */
+ else {
+ msg_syslog_disable();
}
- if (service_path != import_service_path)
- myfree(service_path);
}
/* NAME
/* maillog_client 3h
/* SUMMARY
-/* log manager
+/* choose between syslog client and postlog client
/* SYNOPSIS
/* #include <maillog_client.h>
/* DESCRIPTION
/* 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.
+/* Enable \fBinit\fR mode: do not become a session or process
+/* group leader; similar to \fB-s\fR, do not redirect stdout
+/* to /dev/null, so that "maillog_file = /dev/stdout" works.
+/* This mode is allowed only if the process ID equals 1.
+/* .IP \fB-s\fR
+/* Do not redirect stdout to /dev/null, so that "maillog_file
+/* = /dev/stdout" works.
/* .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
VSTRING *data_lock_path;
off_t inherited_limit;
int debug_me = 0;
+ int keep_stdout = 0;
int ch;
int fd;
int n;
/*
* Process JCL.
*/
- while ((ch = GETOPT(argc, argv, "c:Dde:itvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "c:Dde:istvw")) > 0) {
switch (ch) {
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
if (getpid() != 1)
msg_fatal("-i is allowed only for PID 1 process");
init_mode = 1;
+ keep_stdout = 1;
break;
case 'D':
- debug_me = 1;
+ debug_me = 1;
+ break;
+ case 's':
+ keep_stdout = 1;
break;
case 't':
test_lock = 1;
*/
if (master_detach)
for (fd = 0; fd < 3; fd++) {
+ if (fd == STDOUT_FILENO && keep_stdout)
+ continue;
(void) close(fd);
if (open("/dev/null", O_RDWR, 0) != fd)
msg_fatal("open /dev/null: %m");
* System library.
*/
#include <sys_defs.h>
-#include <syslog.h> /* TEMPORARY */
/*
* Utility library.
#include <logwriter.h>
#include <msg.h>
#include <msg_logger.h>
+#include <stringops.h>
#include <vstream.h>
/*
* Global library.
*/
#include <mail_params.h>
+#include <mail_task.h>
#include <mail_version.h>
+#include <maillog_client.h>
/*
* Server skeleton.
char **unused_argv)
{
- /*
- * This service may still receive messages after "postfix reload" with a
- * configuration that removes the maillog_file setting. Those messages
- * will have to be syslogged instead.
- *
- * XXX When forwarding to syslogd(8), don't bother stripping the time stamp
- * from the preformatted record: we'd have to deal with short records. If
- * we must make our presence invisible, msg_logger(3) should send time in
- * seconds, and leave the formatting to postlogd(8).
- */
if (postlogd_stream) {
(void) logwriter_write(postlogd_stream, buf, len);
- } else {
- /* Until msg_logger has a 'shut up' feature. */
- syslog(LOG_MAIL | LOG_INFO, "%.*s", (int) len, buf); /* TEMPORARY */
+ }
+
+ /*
+ * After a configuration change that removes the maillog_file pathname,
+ * this service may still receive messages (after "postfix reload" or
+ * after process refresh) from programs that use the old maillog_file
+ * setting. Redirect those messages to the current logging mechanism.
+ */
+ else {
+ char *bp = buf;
+ char *progname_pid;
+
+ /*
+ * Avoid surprises: strip off the date, time, host, and program[pid]:
+ * prefix that were prepended by msg_logger(3). Then, hope that the
+ * current logging driver suppresses its own PID, when it sees that
+ * there is a PID embedded in the 'program name'.
+ */
+ (void) mystrtok(&bp, CHARS_SPACE); /* month */
+ (void) mystrtok(&bp, CHARS_SPACE); /* day */
+ (void) mystrtok(&bp, CHARS_SPACE); /* time */
+ (void) mystrtok(&bp, CHARS_SPACE); /* host */
+ progname_pid = mystrtok(&bp, ":" CHARS_SPACE); /* name[pid] sans ':' */
+ bp += strspn(bp, CHARS_SPACE);
+ if (progname_pid)
+ maillog_client_init(progname_pid, 0);
+ msg_info("%.*s", (int) (len - (bp - buf)), bp);
+
+ /*
+ * Restore the program name, in case postlogd(8) needs to log
+ * something about itself. We have to call maillog_client_init() in
+ * any case, because neither msg_syslog_init() nor openlog() make a
+ * copy of the name argument. We can't leave that pointing into the
+ * middle of the above message buffer.
+ */
+ maillog_client_init(mail_task((char *) 0), 0);
}
}
msg_fatal("unexpected command-line argument: %s", argv[0]);
/*
- * This service may still receive messages after "postfix reload" into a
- * configuration that no longer specifies a maillog file. Those messages
- * will have to be syslogged instead.
+ * After a configuration change that removes the maillog_file pathname,
+ * this service may still receive messages from processes that still use
+ * the old configuration. Those messages will have to be redirected to
+ * the current logging subsystem.
*/
if (*var_maillog_file != 0) {
msg_syslog.o: msg_output.h
msg_syslog.o: msg_syslog.c
msg_syslog.o: msg_syslog.h
+msg_syslog.o: mymalloc.h
msg_syslog.o: safe.h
msg_syslog.o: stringops.h
msg_syslog.o: sys_defs.h
/* const char *unix_path,
/* void (*fallback)(const char *))
/*
-/* msg_logger_control(
+/* void msg_logger_control(
/* int key,...)
/* DESCRIPTION
/* This module implements support to report msg(3) diagnostics
/* .fi
/*
/* msg_logger_init() arranges that subsequent msg(3) calls
-/* will write to an internal logging service.
+/* will write to an internal logging service. This function
+/* may also be used to update msg_logger settings.
/*
/* Arguments:
/* .IP progname
/* to the logwriter(3) module.
/* .PP
/* msg_logger_control() makes adjustments to the msg_logger
-/* client. The arguments are a list of macros with zero or
-/* more arguments, terminated with CA_MSG_LOGGER_CTL_END
-/* which has none. The following lists the names and the types
-/* of the corresponding value arguments.
+/* client. These adjustments remain in effect until the next
+/* msg_logger_init() or msg_logger_control() call. The arguments
+/* are a list of macros with zero or more arguments, terminated
+/* with CA_MSG_LOGGER_CTL_END which has none. The following
+/* lists the names and the types of the corresponding value
+/* arguments.
/*
/* Arguments:
/* .IP CA_MSG_LOGGER_CTL_FALLBACK_ONLY
/* Disable the logging socket, and use the fallback function
-/* only. This cannot be changed with msg_logger_init().
+/* only. This remains in effect until the next msg_logger_init()
+/* call.
/* .IP CA_MSG_LOGGER_CTL_FALLBACK(void (*)(const char *))
/* Override the fallback setting (see above) with the specified
-/* function pointer. This cannot be changed with msg_logger_init().
+/* function pointer. This remains in effect until the next
+/* msg_logger_init() or msg_logger_control() call.
+/* .IP CA_MSG_LOGGER_CTL_DISABLE
+/* Disable the msg_logger. This remains in effect until the
+/* next msg_logger_init() call.
/* SEE ALSO
-/* msg(3) diagnostics module
+/* msg(3) diagnostics module
/* BUGS
/* Output records are truncated to ~2000 characters, because
/* unlimited logging is a liability.
static char *msg_logger_hostname;
static char *msg_logger_unix_path;
static void (*msg_logger_fallback_fn) (const char *);
-
- /*
- * Saved state from msg_logger_control().
- */
static int msg_logger_fallback_only_override = 0;
-static int msg_logger_fallback_fn_override = 0;
+static int msg_logger_enable = 0;
/*
* Other state.
struct tm *lt;
ssize_t len;
+ /*
+ * This test is simple enough that we don't bother with unregistering the
+ * msg_logger_print() function.
+ */
+ if (msg_logger_enable == 0)
+ return;
+
/*
* TODO: this should be a reusable NAME_CODE table plus lookup function.
*/
"info", "warning", "error", "fatal", "panic",
};
+ /*
+ * Note: there is code in postlogd(8) that attempts to strip off
+ * information that is prepended here. If the formatting below is
+ * changed, then postlogd needs to be updated as well.
+ */
+
/*
* Format the time stamp.
*/
* XXX If this program is set-gid, then TZ must not be trusted. This
* scrubbing code is in the wrong place.
*/
- if (unsafe())
- while (getenv("TZ")) /* There may be multiple. */
- if (unsetenv("TZ") < 0) { /* Desperate measures. */
- environ[0] = 0;
- msg_fatal("unsetenv: %m");
- }
- tzset();
+ if (first_call) {
+ if (unsafe())
+ while (getenv("TZ")) /* There may be multiple. */
+ if (unsetenv("TZ") < 0) { /* Desperate measures. */
+ environ[0] = 0;
+ msg_fatal("unsetenv: %m");
+ }
+ tzset();
+ }
/*
* Save the request info. Use free-after-update because this data will be
* accessed when mystrdup() runs out of memory.
*/
-#define FREE_AND_UPDATE(dst, src) do { \
- char *_bak = (dst); \
- (dst) = mystrdup(src); \
- if ((_bak)) myfree(_bak); \
+#define UPDATE_AND_FREE(dst, src) do { \
+ if ((dst) == 0 || strcmp((dst), (src)) != 0) { \
+ char *_bak = (dst); \
+ (dst) = mystrdup(src); \
+ if ((_bak)) myfree(_bak); \
+ } \
} while (0)
- FREE_AND_UPDATE(msg_logger_progname, progname);
- FREE_AND_UPDATE(msg_logger_hostname, hostname);
- FREE_AND_UPDATE(msg_logger_unix_path, unix_path);
- if (msg_logger_fallback_fn_override == 0)
- msg_logger_fallback_fn = fallback;
+ UPDATE_AND_FREE(msg_logger_progname, progname);
+ UPDATE_AND_FREE(msg_logger_hostname, hostname);
+ UPDATE_AND_FREE(msg_logger_unix_path, unix_path);
+ msg_logger_fallback_fn = fallback;
/*
* One-time activity: register the output handler, and allocate a buffer.
msg_output(msg_logger_print);
msg_logger_buf = vstring_alloc(2048);
}
+
+ /*
+ * Always.
+ */
+ msg_logger_enable = 1;
+ msg_logger_fallback_only_override = 0;
}
/* msg_logger_control - tweak the client */
va_list ap;
/*
- * We use one-way overrides, because a reversible implementation would be
- * difficult to verify (i.e. it would have bugs).
+ * Overrides remain in effect until the next msg_logger_init() or
+ * msg_logger_control() call,
*/
for (va_start(ap, name); name != MSG_LOGGER_CTL_END; name = va_arg(ap, int)) {
switch (name) {
}
break;
case MSG_LOGGER_CTL_FALLBACK_FN:
- msg_logger_fallback_fn_override = 1;
msg_logger_fallback_fn = va_arg(ap, MSG_LOGGER_FALLBACK_FN);
break;
+ case MSG_LOGGER_CTL_DISABLE:
+ msg_logger_enable = 0;
+ break;
default:
msg_panic("%s: bad name %d", myname, name);
}
#define MSG_LOGGER_CTL_END 0
#define MSG_LOGGER_CTL_FALLBACK_ONLY 1
#define MSG_LOGGER_CTL_FALLBACK_FN 2
+#define MSG_LOGGER_CTL_DISABLE 3
/* Safer API: type-checked arguments, external use. */
#define CA_MSG_LOGGER_CTL_END MSG_LOGGER_CTL_END
#define CA_MSG_LOGGER_CTL_FALLBACK_FN(v) \
MSG_LOGGER_CTL_FALLBACK_FN, CHECK_VAL(MSG_LOGGER_CTL, \
MSG_LOGGER_FALLBACK_FN, (v))
+#define CA_MSG_LOGGER_CTL_DISABLE MSG_LOGGER_CTL_DISABLE
CHECK_VAL_HELPER_DCL(MSG_LOGGER_CTL, MSG_LOGGER_FALLBACK_FN);
/* int log_opt;
/* int facility;
/*
-/* int msg_syslog_facility(facility_name)
+/* int msg_syslog_set_facility(facility_name)
/* const char *facility_name;
+/*
+/* void msg_syslog_disable(void)
/* DESCRIPTION
/* This module implements support to report msg(3) diagnostics
/* via the syslog daemon.
/*
/* msg_syslog_init() is a wrapper around the openlog(3) routine
/* that directs subsequent msg(3) output to the syslog daemon.
+/* This function may also be called to update msg_syslog
+/* settings. If the program name appears to contain a process ID
+/* then msg_syslog_init will attempt to suppress its own PID.
/*
-/* msg_syslog_facility() is a helper routine that overrides the
+/* msg_syslog_set_facility() is a helper routine that overrides the
/* logging facility that is specified with msg_syslog_init().
/* The result is zero in case of an unknown facility name.
+/*
+/* msg_syslog_disable() turns off the msg_syslog client,
+/* until a subsequent msg_syslog_init() call.
/* SEE ALSO
/* syslog(3) syslog library
/* msg(3) diagnostics module
#include "msg_output.h"
#include "msg_syslog.h"
#include "safe.h"
+#include <mymalloc.h>
/*
* Stay a little below the 2048-byte limit of older syslog()
0,
};
-static int syslog_facility;
+static int msg_syslog_facility;
+static int msg_syslog_enable;
/* msg_syslog_print - log info to syslog daemon */
"info", "warning", "error", "fatal", "panic",
};
+ if (msg_syslog_enable == 0)
+ return;
+
if (level < 0 || level >= (int) (sizeof(log_level) / sizeof(log_level[0])))
msg_panic("msg_syslog_print: invalid severity level: %d", level);
if (level == MSG_INFO) {
- syslog(syslog_facility | log_level[level], "%.*s",
+ syslog(msg_syslog_facility | log_level[level], "%.*s",
(int) MSG_SYSLOG_RECLEN, text);
} else {
- syslog(syslog_facility | log_level[level], "%s: %.*s",
+ syslog(msg_syslog_facility | log_level[level], "%s: %.*s",
severity_name[level], (int) MSG_SYSLOG_RECLEN, text);
}
}
* XXX If this program is set-gid, then TZ must not be trusted. This
* scrubbing code is in the wrong place.
*/
- if (unsafe())
- while (getenv("TZ")) /* There may be multiple. */
- if (unsetenv("TZ") < 0) { /* Desperate measures. */
- environ[0] = 0;
- msg_fatal("unsetenv: %m");
- }
- tzset();
+ if (first_call) {
+ if (unsafe())
+ while (getenv("TZ")) /* There may be multiple. */
+ if (unsetenv("TZ") < 0) { /* Desperate measures. */
+ environ[0] = 0;
+ msg_fatal("unsetenv: %m");
+ }
+ tzset();
+ }
+ /* Hack for internal logging forwarding after config change. */
+ if (strchr(name, '[') != 0)
+ logopt &= ~LOG_PID;
openlog(name, LOG_NDELAY | logopt, facility);
if (first_call) {
first_call = 0;
msg_output(msg_syslog_print);
}
+ msg_syslog_enable = 1;
}
-/* msg_syslog_facility - set logging facility by name */
+/* msg_syslog_set_facility - set logging facility by name */
-int msg_syslog_facility(const char *facility_name)
+int msg_syslog_set_facility(const char *facility_name)
{
struct facility_list *fnp;
for (fnp = facility_list; fnp->name; ++fnp) {
if (!strcmp(fnp->name, facility_name)) {
- syslog_facility = fnp->facility;
+ msg_syslog_facility = fnp->facility;
return (1);
}
}
return 0;
}
+/* msg_syslog_disable - disable the msg_syslog client */
+
+void msg_syslog_disable(void)
+{
+ msg_syslog_enable = 0;
+}
+
#ifdef TEST
/*
* External interface.
*/
extern void msg_syslog_init(const char *, int, int);
-extern int msg_syslog_facility(const char *);
+extern int msg_syslog_set_facility(const char *);
+extern void msg_syslog_disable(void);
/* LICENSE
/* .ad
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#endif
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
msg_fatal("%s: pipe: %m", myname);
non_blocking(watchdog_pipe[0], NON_BLOCKING);
non_blocking(watchdog_pipe[1], NON_BLOCKING);
+ close_on_exec(watchdog_pipe[0], CLOSE_ON_EXEC); /* Fix 20190126 */
+ close_on_exec(watchdog_pipe[1], CLOSE_ON_EXEC); /* Fix 20190126 */
event_enable_read(watchdog_pipe[0], watchdog_read, (void *) 0);
}
#endif