From: Wietse Venema Date: Sat, 26 Jan 2019 05:00:00 +0000 (-0500) Subject: postfix-3.4-20190126-nonprod-logger X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ba055e11857bc2613d9644eda64a84d94892a56;p=thirdparty%2Fpostfix.git postfix-3.4-20190126-nonprod-logger --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 0ed284c72..04c38edd2 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -23964,8 +23964,24 @@ Apologies for any names omitted. 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. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index ea9008ef2..7c48a6e01 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -25,6 +25,13 @@ more recent Eclipse Public License 2.0. Recipients can choose to take 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 ============================================ diff --git a/postfix/conf/postfix-script b/postfix/conf/postfix-script index fa3893c2c..07286956d 100755 --- a/postfix/conf/postfix-script +++ b/postfix/conf/postfix-script @@ -158,7 +158,7 @@ start|start-fg) 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" diff --git a/postfix/html/master.8.html b/postfix/html/master.8.html index 64d6735d4..66087d344 100644 --- a/postfix/html/master.8.html +++ b/postfix/html/master.8.html @@ -47,23 +47,26 @@ MASTER(8) MASTER(8) Terminate the master process after exit_time seconds. Child pro- cesses terminate at their convenience. - -i Enable init 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. + -i Enable init mode: do not become a session or process group + leader; similar to -s, 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. - -t Test mode. Return a zero exit status when the master.pid lock - file does not exist or when that file is not locked. This is + -s Do not redirect stdout to /dev/null, so that "maillog_file = + /dev/stdout" works. + + -t Test mode. Return a zero exit status when the master.pid lock + file does not exist or when that file is not locked. This is evidence that the master(8) daemon is not running. - -v Enable verbose logging for debugging purposes. This option is + -v Enable verbose logging for debugging purposes. This option is passed on to child processes. Multiple -v options make the soft- ware increasingly verbose. -w 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. @@ -71,27 +74,27 @@ MASTER(8) MASTER(8) Signals: SIGHUP Upon receipt of a HUP signal (e.g., after "postfix reload"), the - master process re-reads its configuration files. If a service - has been removed from the master.cf 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 master.cf 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. SIGTERM Upon receipt of a TERM signal (e.g., after "postfix abort"), 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 ("postfix stop") 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 ("postfix stop") and allow running processes to finish what they are doing. DIAGNOSTICS - Problems are reported to syslogd(8). The exit status is non-zero in - case of problems, including problems while initializing as a master + Problems are reported to syslogd(8). The exit status is non-zero in + case of problems, including problems while initializing as a master daemon process in the background. ENVIRONMENT MAIL_DEBUG - After initialization, start a debugger as specified with the + After initialization, start a debugger as specified with the debugger_command configuration parameter in the main.cf configu- ration file. @@ -99,18 +102,18 @@ MASTER(8) MASTER(8) Directory with Postfix configuration files. CONFIGURATION PARAMETERS - Unlike most Postfix daemon processes, the master(8) server does not - automatically pick up changes to main.cf. Changes to master.cf are - never picked up automatically. Use the "postfix reload" command after + Unlike most Postfix daemon processes, the master(8) server does not + automatically pick up changes to main.cf. Changes to master.cf are + never picked up automatically. Use the "postfix reload" command after a configuration change. RESOURCE AND RATE CONTROLS default_process_limit (100) - The default maximal number of Postfix child processes that pro- + The default maximal number of Postfix child processes that pro- vide a given service. max_idle (100s) - 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. max_use (100) @@ -118,18 +121,18 @@ MASTER(8) MASTER(8) process will service before terminating voluntarily. service_throttle_time (60s) - How long the Postfix master(8) waits before forking a server + How long the Postfix master(8) waits before forking a server that appears to be malfunctioning. Available in Postfix version 2.6 and later: master_service_disable (empty) - Selectively disable master(8) listener ports by service type or + Selectively disable master(8) listener ports by service type or by service name and type. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_directory (see 'postconf -d' output) @@ -140,20 +143,20 @@ MASTER(8) MASTER(8) invoked with the -D option. inet_interfaces (all) - The network interface addresses that this mail system receives + The network interface addresses that this mail system receives mail on. inet_protocols (all) - The Internet protocols Postfix will attempt to use when making + The Internet protocols Postfix will attempt to use when making or accepting connections. import_environment (see 'postconf -d' output) - 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. mail_owner (postfix) - 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. process_id (read-only) @@ -169,7 +172,7 @@ MASTER(8) MASTER(8) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - 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: @@ -178,7 +181,7 @@ MASTER(8) MASTER(8) The master.cf service name of a Postfix daemon process. FILES - 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 "postconf config_directory" etc. $config_directory/main.cf, global configuration file. diff --git a/postfix/man/man8/master.8 b/postfix/man/man8/master.8 index 6594edc08..c6790ea4a 100644 --- a/postfix/man/man8/master.8 +++ b/postfix/man/man8/master.8 @@ -44,11 +44,13 @@ for debugging only. 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 diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 0bda525c0..a504cce05 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -830,7 +830,7 @@ void mail_params_init() */ 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); diff --git a/postfix/src/global/mail_task.c b/postfix/src/global/mail_task.c index b1df611d4..0049b336a 100644 --- a/postfix/src/global/mail_task.c +++ b/postfix/src/global/mail_task.c @@ -15,8 +15,10 @@ /* 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 @@ -26,6 +28,11 @@ /* 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. */ @@ -52,15 +59,17 @@ const char *mail_task(const char *argv0) 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)); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 98e62efeb..4260eb400 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * 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 diff --git a/postfix/src/global/maillog_client.c b/postfix/src/global/maillog_client.c index dcbadb270..45ce9d2dd 100644 --- a/postfix/src/global/maillog_client.c +++ b/postfix/src/global/maillog_client.c @@ -2,7 +2,7 @@ /* NAME /* maillog_client 3 /* SUMMARY -/* syslog client or internal log client +/* choose between syslog client and postlog client /* SYNOPSIS /* #include /* @@ -10,17 +10,18 @@ /* 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 @@ -53,16 +54,17 @@ /* .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 @@ -106,7 +108,6 @@ * Application-specific. */ static int maillog_client_flags; -static int using_syslog; #define POSTLOG_SERVICE_ENV "POSTLOG_SERVICE" #define POSTLOG_HOSTNAME_ENV "POSTLOG_HOSTNAME" @@ -117,11 +118,18 @@ static void maillog_client_logwriter_fallback(const char *text) { 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); } } @@ -129,100 +137,142 @@ static void maillog_client_logwriter_fallback(const char *text) 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); } diff --git a/postfix/src/global/maillog_client.h b/postfix/src/global/maillog_client.h index cb83c0dfe..71601873b 100644 --- a/postfix/src/global/maillog_client.h +++ b/postfix/src/global/maillog_client.h @@ -5,7 +5,7 @@ /* NAME /* maillog_client 3h /* SUMMARY -/* log manager +/* choose between syslog client and postlog client /* SYNOPSIS /* #include /* DESCRIPTION diff --git a/postfix/src/master/master.c b/postfix/src/master/master.c index 050063b2e..dbe880964 100644 --- a/postfix/src/master/master.c +++ b/postfix/src/master/master.c @@ -38,11 +38,13 @@ /* 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 @@ -263,6 +265,7 @@ int main(int argc, char **argv) VSTRING *data_lock_path; off_t inherited_limit; int debug_me = 0; + int keep_stdout = 0; int ch; int fd; int n; @@ -341,7 +344,7 @@ int main(int argc, char **argv) /* * 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) @@ -357,9 +360,13 @@ int main(int argc, char **argv) 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; @@ -407,6 +414,8 @@ int main(int argc, char **argv) */ 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"); diff --git a/postfix/src/postlogd/postlogd.c b/postfix/src/postlogd/postlogd.c index f8bbf9a4a..0fe1500d0 100644 --- a/postfix/src/postlogd/postlogd.c +++ b/postfix/src/postlogd/postlogd.c @@ -74,7 +74,6 @@ * System library. */ #include -#include /* TEMPORARY */ /* * Utility library. @@ -82,13 +81,16 @@ #include #include #include +#include #include /* * Global library. */ #include +#include #include +#include /* * Server skeleton. @@ -124,21 +126,44 @@ static void postlogd_service(char *buf, ssize_t len, char *unused_service, 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); } } @@ -162,9 +187,10 @@ static void pre_jail_init(char *unused_service_name, char **argv) 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) { diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index bc2361129..381bb6e13 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -2051,6 +2051,7 @@ msg_syslog.o: msg.h 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 diff --git a/postfix/src/util/msg_logger.c b/postfix/src/util/msg_logger.c index 1725d46b6..c9cd9061e 100644 --- a/postfix/src/util/msg_logger.c +++ b/postfix/src/util/msg_logger.c @@ -12,7 +12,7 @@ /* 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 @@ -24,7 +24,8 @@ /* .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 @@ -42,20 +43,27 @@ /* 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. @@ -100,12 +108,8 @@ static char *msg_logger_progname; 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. @@ -135,6 +139,13 @@ static void msg_logger_print(int level, const char *text) 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. */ @@ -145,6 +156,12 @@ static void msg_logger_print(int level, const char *text) "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. */ @@ -212,29 +229,32 @@ void msg_logger_init(const char *progname, const char *hostname, * 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. @@ -244,6 +264,12 @@ void msg_logger_init(const char *progname, const char *hostname, 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 */ @@ -254,8 +280,8 @@ void msg_logger_control(int name,...) 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) { @@ -267,9 +293,11 @@ void msg_logger_control(int 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); } diff --git a/postfix/src/util/msg_logger.h b/postfix/src/util/msg_logger.h index 9f8c74005..e135e7e38 100644 --- a/postfix/src/util/msg_logger.h +++ b/postfix/src/util/msg_logger.h @@ -34,6 +34,7 @@ extern void msg_logger_control(int,...); #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 @@ -41,6 +42,7 @@ extern void msg_logger_control(int,...); #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); diff --git a/postfix/src/util/msg_syslog.c b/postfix/src/util/msg_syslog.c index df3394b7b..7c979c66d 100644 --- a/postfix/src/util/msg_syslog.c +++ b/postfix/src/util/msg_syslog.c @@ -11,18 +11,26 @@ /* 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 @@ -64,6 +72,7 @@ #include "msg_output.h" #include "msg_syslog.h" #include "safe.h" +#include /* * Stay a little below the 2048-byte limit of older syslog() @@ -143,7 +152,8 @@ static struct facility_list facility_list[] = { 0, }; -static int syslog_facility; +static int msg_syslog_facility; +static int msg_syslog_enable; /* msg_syslog_print - log info to syslog daemon */ @@ -156,14 +166,17 @@ static void msg_syslog_print(int level, const char *text) "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); } } @@ -179,35 +192,48 @@ void msg_syslog_init(const char *name, int logopt, int facility) * 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 /* diff --git a/postfix/src/util/msg_syslog.h b/postfix/src/util/msg_syslog.h index 614daaecf..d0441bbd6 100644 --- a/postfix/src/util/msg_syslog.h +++ b/postfix/src/util/msg_syslog.h @@ -19,7 +19,8 @@ * 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 @@ -30,6 +31,11 @@ extern int msg_syslog_facility(const char *); /* 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 diff --git a/postfix/src/util/watchdog.c b/postfix/src/util/watchdog.c index 187932a48..3ec1fbc5f 100644 --- a/postfix/src/util/watchdog.c +++ b/postfix/src/util/watchdog.c @@ -74,6 +74,11 @@ /* 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. */ @@ -216,6 +221,8 @@ WATCHDOG *watchdog_create(unsigned timeout, WATCHDOG_FN action, char *context) 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