aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-10-23 13:02:08 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-10-23 13:02:08 +0000
commit9e569139583178c7e1c35147cef3fd17493a11da (patch)
tree36defb2a0547176e1ad91b8fc653f5dc687c9da9
parent80c8065d88f8c0621d6b690976dc675092db6509 (diff)
downloadmailfromd-9e569139583178c7e1c35147cef3fd17493a11da.tar.gz
mailfromd-9e569139583178c7e1c35147cef3fd17493a11da.tar.bz2
Reimplement syslog-async. Thanks Simon Kelley for relicensing it under GPLv3.
git-svn-id: file:///svnroot/mailfromd/trunk@1520 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r--ChangeLog7
-rw-r--r--NEWS24
-rw-r--r--configure.ac25
-rw-r--r--doc/mailfromd.texi83
-rw-r--r--src/Makefile.am4
-rw-r--r--src/main.c69
-rw-r--r--src/syslog_async.c413
-rw-r--r--src/syslog_async.h126
8 files changed, 719 insertions, 32 deletions
diff --git a/ChangeLog b/ChangeLog
index 053fa59c..c47d8ed7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,13 @@
+2007-10-23 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ * configure.ac, src/syslog_async.c, src/syslog_async.h,
+ src/main.c, src/Makefile.am: Reimplement syslog-async. Thanks
+ Simon Kelley for relicensing it under GPLv3.
+ * doc/mailfromd.texi, NEWS: Update
+
2007-10-21 Sergey Poznyakoff <gray@gnu.org.ua>
* src/dnsbase.c (cname_loop_body): Return TXT only if explicitly
required. Bug reported by Jan Rafaj.
* src/engine.c: Fix timeout calculations
diff --git a/NEWS b/NEWS
index fba2e9ff..578b9670 100644
--- a/NEWS
+++ b/NEWS
@@ -1,40 +1,52 @@
-Mailfromd NEWS -- history of user-visible changes. 2007-09-11
+Mailfromd NEWS -- history of user-visible changes. 2007-10-23
Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
See the end of file for copying conditions.
Please send mailfromd bug reports to <bug-mailfromd@gnu.org.ua>
-Version 4.1.1 (SVN)
+Version 4.2 (SVN)
* Licensed under the GPLv3
-* Syslog-async code has been withdrawn, due to its incompatibility with
-the new license. The similar functionality will probably be introduced
-again when I write it.
+* New command line options --syslog-async and --no-syslog-async
+
+These options allow to select the implementation of syslog to use
+at run time.
* RFC 2821 compatibility
The sender verification engine used to send whitespace character
between `MAIL FROM:' and `RCPT TO:' commands and their argument.
This violated RFC 2821, which requires the argument to follow the
command without any intermediate whitespace. It is fixed in this
release.
* Sample threshold for `rate' function.
The `rate' function takes an optional third argument allowing to
-specify the minimum number of received mails needed to obtain
+specify the minimum number of received mails needed to obtain the
sending rate value. The default is 2, which is probably too
conservative. The following example raises it to 10:
if rate($f "-" ${client_addr}, interval("1 hour"), 10) > %maxrate
...
fi
+* mtasim is improved
+
+In particular, it accepts MAIL FROM: and RCPT TO: without extra space
+after the colon, as required by RFC. The milter interface is also
+improved.
+
+* The `resolve' function ignores TXT records.
+
+In other words, resolve and primitive_resolve are guaranteed to return
+either an A or a PTR record.
+
Version 4.1, 2007-06-11
* National Language Support.
The program includes National Language Support. Polish and Ukrainian
diff --git a/configure.ac b/configure.ac
index 885a804c..e49a654a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,14 +13,14 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.59)
m4_define([MF_VERSION_MAJOR], 4)
-m4_define([MF_VERSION_MINOR], 1)
-m4_define([MF_VERSION_PATCH], 1)
+m4_define([MF_VERSION_MINOR], 2)
+dnl m4_define([MF_VERSION_PATCH], 0)
AC_INIT([mailfromd],
MF_VERSION_MAJOR.MF_VERSION_MINOR[]m4_ifdef([MF_VERSION_PATCH],.MF_VERSION_PATCH),
[bug-mailfromd@gnu.org.ua])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
@@ -121,15 +121,28 @@ fi
AC_SUBST(lisp_LISP)
# Gettext.
AM_GNU_GETTEXT([external], [need-formatstring-macros])
AM_GNU_GETTEXT_VERSION([0.16])
-# Syslog -- removed due to version incompatibility
-if test "${enable_syslog_async+set}" = set; then
- AC_MSG_WARN([The option --enable-syslog-async is no longer supported.])
+# Syslog
+AC_ARG_ENABLE([syslog-async],
+ AC_HELP_STRING([--enable-syslog-async],
+ [use non-blocking version of syslog by default]),
+ [case "${enableval}" in
+ yes) syslog_async=yes ;;
+ no) syslog_async=no ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-syslog-async]) ;;
+ esac],[syslog_async=no])
+
+AH_TEMPLATE([DEFAULT_SYSLOG_ASYNC],
+ [Define to 1 if syslog-async is being used by default])
+if test $syslog_async = "yes"; then
+ AC_DEFINE([DEFAULT_SYSLOG_ASYNC], [1])
+else
+ AC_DEFINE([DEFAULT_SYSLOG_ASYNC], [0])
fi
# Check for DBM flavor
AH_TEMPLATE(BDB2_CURSOR_LASTARG,
[Last argument to the cursor member of Berkeley 2 DB structure])
@@ -557,13 +570,13 @@ DBM version............................... $status_dbm
Default user.............................. $user
State directory........................... $stat_dir
Socket.................................... $socket
Expiration interval....................... $expire
Negative DNS answer expiration interval... $negative_dns_expire
Rates expire interval..................... $rates_expire
-Syslog implementation..................... $syslog_flavor
+Default syslog implementation............. $syslog_flavor
Readline (for mtasim)..................... $usereadline
Documentation rendition type.............. $rendition
*******************************************************************
EOF
],
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 078f722a..6aa44c6b 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -128,12 +128,13 @@ Introduction to @command{mailfromd}
Sender Address Verification.
* Limitations::
Building the Package
+* 410-420:: Upgrading from 4.1 to 4.2
* 400-410:: Upgrading from 4.0 to 4.1
* 31x-400:: Upgrading from 3.1.x to 4.0
* 30x-31x:: Upgrading from 3.0.x to 3.1
* 2x-30x:: Upgrading from 2.x to 3.0.x
* 1x-2x:: Upgrading from 1.x to 2.x
@@ -882,37 +883,41 @@ mail rate database (@pxref{rate}).
Expiration settings can be changed at run time using
@samp{#pragma database} statement in the filter script file
(@pxref{database}).
@cindex enable-syslog-async, @option{--enable-syslog-async}, @command{configure} option
@cindex syslog, non-blocking
-@item Select @command{syslog} implementation to use.
+@item Select a @command{syslog} implementation to use.
@anchor{syslog-async}
@command{Mailfromd} uses @code{syslog} for diagnostics output. The
default @code{syslog} implementation on most systems (most notably, on
GNU/Linux) uses blocking @code{AF_UNIX SOCK_DGRAM} sockets. As a
result, when an application calls @code{syslog()}, and
@command{syslogd} is not responding and the socket buffers get full,
the application will hang.
@cindex Simon Kelley
- In the case of a daemon as @command{mailfromd}, it is more important
+ For @command{mailfromd}, as for any daemon, it is more important
that it continue to run, than that it continue to log. For this
purpose, @command{mailfromd} is shipped with a non-blocking
@code{syslog} implementation by Simon Kelley. This implementation,
instead of blocking, buffers log lines in memory. When the buffer log
overflows, some lines are lost, but the daemon continues to run. When
lines are lost, this fact is logged with a message of the form:
@smallexample
async_syslog overflow: 5 log entries lost
@end smallexample
- To enable non-blocking @command{syslog}, use the
-@option{--enable-syslog-async} command line option:
+ You can select which implementation to run when starting
+@command{mailfromd}, by using @option{--syslog-async} or
+@option{--no-syslog-async} command line options
+(@pxref{Logging and Debugging}). When configuring, you can set
+the asynchronous syslog as the @emph{default} syslog implementation,
+using the @option{--enable-syslog-async} command line option:
@smallexample
./configure --enable-syslog-async
@end smallexample
@item Run @command{configure} with all the desired options.
@@ -942,13 +947,13 @@ Default user.............................. mail
State directory...........................
$(localstatedir)/$(PACKAGE)
Socket.................................... mailfrom
Expiration interval....................... 86400
Negative DNS answer expiration interval... 3600
Rates expire interval..................... 300
-Syslog implementation..................... blocking
+Default syslog implementation............. blocking
Readline (for mtasim)..................... yes
Documentation rendition type.............. PROOF
*******************************************************************
@end group
@end smallexample
@@ -966,19 +971,36 @@ and mode.
(@file{@var{sysconfdir}/mailfromd.etc}) and edit it, if necessary. If
you are upgrading from an older version of @command{mailfromd}, see
the corresponding section below.
@end enumerate
@menu
+* 410-420:: Upgrading from 4.1 to 4.2
* 400-410:: Upgrading from 4.0 to 4.1
* 31x-400:: Upgrading from 3.1.x to 4.0
* 30x-31x:: Upgrading from 3.0.x to 3.1
* 2x-30x:: Upgrading from 2.x to 3.0.x
* 1x-2x:: Upgrading from 1.x to 2.x
@end menu
+@node 410-420
+@section Upgrading from 4.1 to 4.2
+@cindex Upgrading from 4.1 to 4.2
+@UNREVISED{}
+ Upgrading to this version does not require any special efforts. You
+can use your configuration files and filter scripts without any
+changes. The only difference worth noticing is that starting from this
+version @command{mailfromd} is always compiled with asynchronous
+syslog implementation. The @option{--enable-syslog-async}
+configuration file option is still available, but its meaning has
+changed: it sets the @emph{default} syslog implementation to use
+(@pxref{syslog-async}). Thus, it can be used the same way it was in
+previous versions. You can also select the syslog implementation at
+run time, see @ref{Logging and Debugging, --syslog-async option}, for
+more detailed information.
+
@node 400-410
@section Upgrading from 4.0 to 4.1
@cindex Upgrading from 4.0 to 4.1
Upgrading to this version does not require any special efforts. You
can use your configuration files and filter scripts without any changes.
@@ -1008,13 +1030,13 @@ Pragma options: @code{retry}, @code{io-retry}, and
@node 31x-400
@section Upgrading from 3.1.x to 4.0
@cindex upgrading from 3.1.x to 4.0
Before building this version, please re-read the chapter
-@xref{Building}, especially the section @xref{syslog-async, Using
+@xref{Building}, especially the section @ref{syslog-async, Using
non-blocking syslog}.
Starting from the version 4.0, @acronym{MFL} no longer uses the
predefined symbolic names for exception codes (previous versions used
the @samp{&} prefix to dereference them). Instead, it relies on
constants defined in the include file @file{status.mfh}
@@ -2153,21 +2175,21 @@ representing the average number of messages per second sent by this
the sender email address can be used as a @code{key}, however we recommend
to use a conjunction @var{email}-@var{sender_ip} instead, so the
actual @var{email} owner won't be blocked by actions of some spammer
abusing his/her address.
To control and update sending rates, the @code{rate} function is
-provided. It takes two arguments: @code{key}, whose meaning is
-described above, and @code{interval}, or the number of seconds, to
+provided. It takes two mandatory arguments: @code{key}, whose meaning
+is described above, and @code{interval}, or the number of seconds, to
which the actual sending rate value is converted. Remember, that it is
stored internally as a floating point number, and thus cannot be used
in @command{mailfromd} filters, which operate only on integer numbers.
To use the rate value, it is first converted to messages per given
interval, which is an integer number. For example, the rate
@code{0.138888} brought to 1-hour interval gives @code{500}
-(messages per hour).
+(messages per hour).
Wherever the @code{rate} function is called, it recomputes and
updates the rate record for the given @var{key}, and returns its
value, converted to messages per interval. For example, the following
code limits the mail sending rate for each @samp{email
address}-@samp{@acronym{IP}} combination to 180 per hour. If the
@@ -2213,12 +2235,13 @@ with the given key value were detected:
@smallexample
rate($f "-" $@{client_addr@}, interval("1 hour"), 10)
@end smallexample
For additional information about @code{rate} function, see @ref{rate}.
+
@node Greylisting
@section Greylisting
Greylisting is a simple method of defending against the spam
proposed by Evan Harris. In few words, it consists in recording the
@samp{sender @acronym{IP}}-@samp{sender email}-@samp{recipient email} triplet of
@@ -2417,13 +2440,13 @@ see a similar output:
version: @value{VERSION}
script file: /etc/mailfromd.rc
user: mail
statedir: /var/run/mailfromd
socket: unix:/var/run/mailfromd/mailfrom
pidfile: /var/run/mailfromd/mailfromd.pid
-syslog: blocking
+default syslog: blocking
database format: Berkeley DB 2.x
dns database: /var/run/mailfromd/dns.db
dns negative expiration: 3600
cache database: /var/run/mailfromd/mailfromd.db
cache expiration: 86400
cache negative expiration: 43200
@@ -2827,12 +2850,30 @@ standard error is used if the program is given one of the database
management options (@pxref{Databases}, or @option{--test} option
(@pxref{Testing Filter Scripts}. Otherwise, syslog is used. To alter
these defaults, two command line options are provided:
@option{--stderr} to print everything to standard error and
@option{--syslog} to output all diagnostics to syslog.
+@cindex syslog, default implementation
+@cindex syslog, non-blocking
+@cindex syslog, asynchronous
+@cindex asynchronous syslog
+@cindex non-blocking syslog
+@xopindex{syslog-async, introduced}
+@xopindex{no-syslog-async, introduced}
+ The data can be sent to syslog using two ways: the @code{syslog}
+function from the system @file{libc} library, which is a @dfn{blocking}
+implementation in most cases, and the internal, @dfn{asynchronous}
+syslog implementation. @xref{syslog-async, Using non-blocking syslog},
+for more information on these implementations and for information on
+how to select the default one. Two options are provided to select the
+implementation to use at run time: the @option{--syslog-async} option
+instructs @command{mailfromd} to use asynchronous version, and the
+@option{--no-syslog-async} option instructs it to use the blocking
+version.
+
@cindex syslog facility, selecting
@cindex selecting syslog facility
@cindex syslog facility, default
@cindex default syslog facility
The default syslog facility is @samp{mail}, and it can be changed
by @option{--log-facility} option. Argument to this option is a valid
@@ -10407,12 +10448,23 @@ debug the protocol.
Output logs to syslog @var{facility}.
@opsummary{log-tag}
@item --log-tag=@var{string}
Tag syslog entries with the given @var{string}, instead of the program name.
+@opsummary{no-syslog-async}
+@item --no-syslog-async
+Use system @command{libc} syslog implementation. @xref{Logging and Debugging},
+for more information about two syslog flavors, and see @ref{syslog-async,
+Using non-blocking syslog}, for information on how to set default
+syslog implementation at compile time.
+
+To inspect the default syslog implementation, use the
+@option{--show-defaults} command line option (@pxref{Databases,
+show-defaults}).
+
@opsummary{stderr}
@item -s
@itemx --stderr
Log to stderr (by default logging goes to syslog).
@xref{Logging and Debugging}.
@@ -10457,12 +10509,23 @@ channel. @xref{Logging and Debugging}.
@opsummary{syslog}
@item --syslog
Output logs to syslog. This is the default, unless any of the
following options is used: @option{--compact}, @option{--delete},
@option{--expire}, @option{--test}, and @option{--list}.
+@opsummary{syslog-async}
+@item --syslog-async
+Use asynchronous syslog implementation. @xref{Logging and Debugging},
+for more information about two syslog flavors, and see @ref{syslog-async,
+Using non-blocking syslog}, for information on how to set default
+syslog implementation at compile time.
+
+To inspect the default syslog implementation, use the
+@option{--show-defaults} command line option (@pxref{Databases,
+show-defaults}).
+
@opsummary{xref}
@item --xref
Same as @option{--dump-xref}. @xref{Logging and Debugging}.
@end table
diff --git a/src/Makefile.am b/src/Makefile.am
index fe86d2b5..ea504d53 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,17 +60,19 @@ mailfromd_SOURCES = \
rate.c\
$(M4_FILES:.m4=.c)
noinst_LIBRARIES=libmf.a
libmf_a_SOURCES=\
+ syslog_async.c\
+ syslog_async.h\
version.c
libmf_a_LIBADD=$(LIBOBJS)
-mailfromd_LDADD = ./libmf.a $(LDADD) $(SYSLOG_LIBS)
+mailfromd_LDADD = ./libmf.a $(LDADD)
mtasim_SOURCES = mtasim.c openat-die.c
mtasim_LDADD = ./libmf.a $(LDADD) $(READLINE_LIBS)
noinst_HEADERS = mailfromd.h mu_dbm.h builtin.h dns.h spf.h drivers.c debug.h
EXTRA_DIST = \
diff --git a/src/main.c b/src/main.c
index c277645f..938a82b6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -36,12 +36,14 @@
#include <mailutils/mailutils.h>
#include <mailutils/argp.h>
#include "mailfromd.h"
+#include "syslog_async.h"
+
/* Configurable options */
int mode = MAILFROMD_DAEMON; /* Default operation mode */
enum smtp_state test_state = smtp_state_envfrom; /* State for --test mode */
int need_config = 1;/* Set if the current mode requires reading the
@@ -73,12 +75,14 @@ char *portspec; /* Communication socket specification.
int force_remove; /* Remove local communication socket if it already
exists */
int foreground; /* Stay in foreground */
int single_process_option; /* Run in single process mode. */
unsigned long source_address = INADDR_ANY; /* Source address for TCP
connections */
+int use_syslog_async = DEFAULT_SYSLOG_ASYNC;
+ /* Use asynchronous syslog implementation */
char *syslog_tag; /* Tag to mark syslog entries with. */
char *mailfromd_state_dir; /* see init_names() */
char *pidfile; /* see init_names() */
char *user = DEFAULT_USER; /* Switch to this user privileges after
startup */
mu_list_t retain_groups; /* List of group IDs to retain when switching
@@ -118,22 +122,50 @@ time_t response_timeout = 30;
/* Logging & debugging */
int
syslog_printer (int prio, const char *fmt, va_list ap)
{
+ if (use_syslog_async) {
+ vsyslog_async (prio, fmt, ap);
+ } else {
#if HAVE_VSYSLOG
- vsyslog (prio, fmt, ap);
+ vsyslog (prio, fmt, ap);
#else
- char buf[128];
- vsnprintf (buf, sizeof buf, fmt, ap);
- syslog (prio, "%s", buf);
+ char buf[128];
+ vsnprintf (buf, sizeof buf, fmt, ap);
+ syslog (prio, "%s", buf);
#endif
+ }
return 0;
}
+void
+mf_gacopyz_syslog_async_log_printer(int level, char *fmt, va_list ap)
+{
+ switch (level) {
+ case SMI_LOG_DEBUG:
+ level = LOG_DEBUG;
+ break;
+ case SMI_LOG_INFO:
+ level = LOG_INFO;
+ break;
+ case SMI_LOG_WARN:
+ level = LOG_WARNING;
+ break;
+ case SMI_LOG_ERR:
+ level = LOG_ERR;
+ break;
+
+ case SMI_LOG_FATAL:
+ default:
+ level = LOG_EMERG;
+ }
+ vsyslog_async(level, fmt, ap);
+}
+
int
syslog_error_printer (const char *fmt, va_list ap)
{
return syslog_printer(LOG_ERR, fmt, ap);
}
@@ -782,22 +814,24 @@ enum mailfromd_option {
OPTION_LIST,
OPTION_LOCK_RETRY_COUNT,
OPTION_LOCK_RETRY_TIMEOUT,
OPTION_MILTER_TIMEOUT,
OPTION_MTASIM,
OPTION_NO_PREPROCESSOR,
+ OPTION_NO_SYSLOG_ASYNC,
OPTION_PIDFILE,
OPTION_POSTMASTER_EMAIL,
OPTION_PREDICT_NEXT,
OPTION_PREPROCESSOR,
OPTION_SHOW_DEFAULTS,
OPTION_SINGLE_PROCESS,
OPTION_STACK_TRACE,
OPTION_STATE_DIRECTORY,
OPTION_SOURCE_INFO,
OPTION_SYSLOG,
+ OPTION_SYSLOG_ASYNC,
OPTION_TIME_FORMAT,
OPTION_TIMEOUT,
OPTION_TRACE,
OPTION_TRACE_PROGRAM,
};
@@ -941,12 +975,16 @@ static struct argp_option options[] = {
{ "gacopyz-log", OPTION_GACOPYZ_LOG, N_("LEVEL"), 0,
N_("Set Gacopyz log level"), GRP+1 },
{ "stderr", 's', NULL, 0,
N_("Log to stderr"), GRP+1 },
{ "syslog", OPTION_SYSLOG, NULL, 0,
N_("Log to syslog (default)"), GRP+1 },
+ { "syslog-async", OPTION_SYSLOG_ASYNC, NULL, 0,
+ N_("Use asynchronous syslog"), GRP+1 },
+ { "no-syslog-async", OPTION_NO_SYSLOG_ASYNC, NULL, 0,
+ N_("Use system syslog"), GRP+1 },
{ "log-tag", OPTION_LOG_TAG, N_("STRING"), 0,
N_("Set the identifier used in syslog messages to STRING"), GRP+1 },
{ "source-info", OPTION_SOURCE_INFO, NULL, 0,
N_("Debug messages include source information"), GRP+1 },
{ "stack-trace", OPTION_STACK_TRACE, NULL, 0,
N_("Enable stack traces on runtime errors"), GRP+1 },
@@ -1260,12 +1298,20 @@ parse_opt (int key, char *arg, struct argp_state *state)
break;
case OPTION_SYSLOG:
log_to_stderr = 0;
break;
+ case OPTION_SYSLOG_ASYNC:
+ use_syslog_async = 1;
+ break;
+
+ case OPTION_NO_SYSLOG_ASYNC:
+ use_syslog_async = 0;
+ break;
+
case OPTION_TIMEOUT:
set_option("timeout", arg, 1);
break;
case OPTION_TRACE:
do_trace = 1;
@@ -1653,16 +1699,16 @@ mailfromd_show_defaults()
printf("script file: %s\n", script_file);
printf("preprocessor: %s\n", ext_pp ? ext_pp : "none");
printf("user: %s\n", user);
printf("statedir: %s\n", mailfromd_state_dir);
printf("socket: %s\n", portspec);
printf("pidfile: %s\n", pidfile);
-#ifdef USE_SYSLOG_ASYNC
- printf("syslog: non-blocking\n");
+#if DEFAULT_SYSLOG_ASYNC == 1
+ printf("default syslog: non-blocking\n");
#else
- printf("syslog: blocking\n");
+ printf("default syslog: blocking\n");
#endif
printf("database format: ");
#if defined WITH_GDBM
printf("GDBM");
#elif defined WITH_BDB
printf("Berkeley DB %d.x", WITH_BDB);
@@ -1674,14 +1720,19 @@ mailfromd_show_defaults()
void
log_setup(int want_stderr)
{
/* Set up logging */
if (!want_stderr) {
- openlog(syslog_tag, LOG_PID, log_facility);
- gacopyz_set_logger(gacopyz_syslog_log_printer);
+ if (use_syslog_async) {
+ openlog_async(syslog_tag, LOG_PID, log_facility);
+ gacopyz_set_logger(mf_gacopyz_syslog_async_log_printer);
+ } else {
+ openlog(syslog_tag, LOG_PID, log_facility);
+ gacopyz_set_logger(gacopyz_syslog_log_printer);
+ }
mu_error_set_print(syslog_error_printer);
} else {
gacopyz_set_logger(gacopyz_stderr_log_printer);
mu_error_set_print(stderr_error_printer);
}
}
diff --git a/src/syslog_async.c b/src/syslog_async.c
new file mode 100644
index 00000000..a168eac9
--- /dev/null
+++ b/src/syslog_async.c
@@ -0,0 +1,413 @@
+/* syslog_async is Copyright (c) 2007 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/file.h>
+#include <sys/syslog.h>
+
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "syslog_async.h"
+
+/* From RFC 3164 */
+#define MAX_MESSAGE 1024
+
+#define DEF_BACKLOG 5
+#define DEF_DELAY 1000 /* doesn't come into effect until backlog > 10 */
+
+static int log_fac = LOG_USER;
+static int log_opts = LOG_ODELAY;
+static const char *log_tag = "syslog";
+static int log_mask = 0xff;
+static int log_backlog = DEF_BACKLOG;
+static int log_delay = DEF_DELAY;
+
+static int log_fd = -1;
+static int entries_alloced = 0;
+static int entries_lost = 0;
+static int connection_good = 1;
+
+struct log_entry {
+ int offset, length;
+ struct log_entry *next;
+ char payload[MAX_MESSAGE];
+};
+
+static struct log_entry *entries = NULL;
+static struct log_entry *free_entries = NULL;
+
+static int mksock(int type)
+{
+ int flags;
+ int fd = socket(AF_UNIX, type, 0);
+
+ if (fd != -1)
+ {
+ if ((flags = fcntl(fd, F_GETFL)) == -1 ||
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
+ (flags = fcntl(fd, F_GETFD)) == -1 ||
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ close(fd);
+ fd = -1;
+ }
+ }
+ return fd;
+}
+
+void openlog_async(const char *ident, int option, int facility)
+{
+ if (ident)
+ log_tag = ident;
+
+ log_opts = option;
+
+ if (facility != 0 && (facility &~ LOG_FACMASK) == 0)
+ log_fac = facility;
+
+ if (log_opts & LOG_NDELAY)
+ log_fd = mksock(SOCK_DGRAM);
+}
+
+int setlogmask_async(int mask)
+{
+ int old = log_mask;
+
+ if (mask != 0)
+ log_mask = mask;
+
+ return old;
+}
+
+void tunelog_async(int backlog, int delay)
+{
+ /* we need at least one buffer, and the
+ delay calculations overflow for more than 99 */
+ if (backlog < 1)
+ backlog = 1;
+ else if (backlog > 99)
+ backlog = 99;
+
+ /* don't lose existing buffers */
+ if (backlog < entries_alloced)
+ log_backlog = entries_alloced;
+ else
+ log_backlog = backlog;
+
+ if (delay < 0)
+ log_delay = 0;
+ else if (delay > 1000)
+ log_delay = 1000;
+ else
+ log_delay = delay;
+}
+
+void closelog_async(void)
+{
+ /* maybe last chance to flush */
+ log_write_async();
+
+ if (log_fd != -1)
+ {
+ close(log_fd);
+ log_fd = -1;
+ }
+
+ /* restore defaults */
+ log_fac = LOG_USER;
+ log_opts = LOG_ODELAY;
+ log_tag = "syslog";
+ log_mask = 0xff;
+
+ log_delay = DEF_DELAY;
+
+ if (entries_alloced < DEF_BACKLOG)
+ log_backlog = entries_alloced;
+ else
+ log_backlog = DEF_BACKLOG;
+}
+
+int log_fd_async(void)
+{
+ if (!entries || !connection_good)
+ return -1;
+
+ return log_fd;
+}
+
+void log_write_async(void)
+{
+ ssize_t rc;
+ int fd, tried_stream = 0;
+ struct log_entry *tmp;
+
+ while (entries)
+ {
+ if (log_fd == -1 &&
+ (log_fd = mksock(SOCK_DGRAM)) == -1)
+ goto fail;
+
+ connection_good = 1;
+
+ if ((rc = send(log_fd,
+ entries->payload + entries->offset,
+ entries->length,
+ MSG_NOSIGNAL)) != -1)
+ {
+ entries->length -= rc;
+ entries->offset += rc;
+ connection_good = 1;
+
+ if (entries->length == 0)
+ goto free;
+
+ continue;
+ }
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN)
+ return;
+
+ /* *BSD, returns this instead of blocking? */
+ if (errno == ENOBUFS)
+ {
+ connection_good = 0;
+ return;
+ }
+
+ /* A stream socket closed at the other end goes into EPIPE
+ forever, close and re-open. */
+ if (errno == EPIPE)
+ goto reopen_stream;
+
+ if (errno == ECONNREFUSED ||
+ errno == ENOTCONN ||
+ errno == EDESTADDRREQ ||
+ errno == ECONNRESET)
+ {
+ /* socket went (syslogd down?), try and reconnect. If we fail,
+ stop trying until the next call to my_syslog()
+ ECONNREFUSED -> connection went down
+ ENOTCONN -> nobody listening
+ (ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
+
+ struct sockaddr_un logaddr;
+
+ logaddr.sun_family = AF_LOCAL;
+ strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
+
+ /* Got connection back? try again. */
+ if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
+ continue;
+
+ /* errors from connect which mean we should keep trying */
+ if (errno == ENOENT ||
+ errno == EALREADY ||
+ errno == ECONNREFUSED ||
+ errno == EISCONN ||
+ errno == EINTR ||
+ errno == EAGAIN)
+ {
+ /* try again on next syslog() call */
+ connection_good = 0;
+ return;
+ }
+
+ /* we start with a SOCK_DGRAM socket, but syslog may want SOCK_STREAM */
+ if (!tried_stream && errno == EPROTOTYPE)
+ {
+ reopen_stream:
+ tried_stream = 1;
+ close(log_fd);
+ if ((log_fd = mksock(SOCK_STREAM)) != -1)
+ continue;
+ }
+ }
+
+ fail:
+ tried_stream = 0;
+
+ /* give up - try to write to console if we've been asked
+ take care not to block in open() or write() */
+ if ((log_opts & LOG_CONS) &&
+ (fd = open(_PATH_CONSOLE, O_WRONLY | O_NONBLOCK, 0)) != -1)
+ {
+ char *start = strchr(entries->payload, '>') + 1;
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags != -1)
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ entries->length -= start - entries->payload;
+ /* move down to remove the tag, and make room for the \r\n */
+ memmove(entries->payload, start, entries->length);
+ entries->payload[entries->length - 1] = '\r';
+ entries->payload[entries->length] = '\n';
+ write(fd, entries->payload, entries->length + 1);
+ close(fd);
+ }
+
+ free:
+ tmp = entries;
+ entries = tmp->next;
+ tmp->next = free_entries;
+ free_entries = tmp;
+
+ if (entries_lost != 0)
+ {
+ int e = entries_lost;
+ entries_lost = 0; /* avoid wild recursion */
+ syslog_async(LOG_WARNING, "async_syslog overflow: %d log entries lost", e);
+ }
+ continue;
+ }
+}
+
+void syslog_async(int priority, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vsyslog_async(priority, format, ap);
+ va_end(ap);
+}
+
+void vsyslog_async(int priority, const char *format, va_list ap)
+{
+ struct log_entry *entry;
+ time_t time_now;
+ char *p, *q, *r;
+ size_t len;
+
+ if (!(log_mask & LOG_MASK(LOG_PRI(priority))) || (priority &~ (LOG_PRIMASK|LOG_FACMASK)))
+ return;
+
+ if ((entry = free_entries))
+ free_entries = entry->next;
+ else if (entries_alloced < log_backlog && (entry = malloc(sizeof(struct log_entry))))
+ entries_alloced++;
+
+ if (!entry)
+ entries_lost++;
+ else
+ {
+ /* add to end of list, consumed from the start */
+ entry->next = NULL;
+ if (!entries)
+ entries = entry;
+ else
+ {
+ struct log_entry *tmp;
+ for (tmp = entries; tmp->next; tmp = tmp->next);
+ tmp->next = entry;
+ }
+
+ time(&time_now);
+ p = entry->payload;
+ p += sprintf(p, "<%d>", priority | log_fac);
+
+ q = p;
+
+ if (log_opts & LOG_PID)
+ p += sprintf(p, "%.15s %s[%d]: ", ctime(&time_now) + 4, log_tag, getpid());
+ else
+ p += sprintf(p, "%.15s %s: ", ctime(&time_now) + 4, log_tag);
+
+ len = p - entry->payload;
+ len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
+ entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
+
+ /* remove trailing '\n's passed to us. */
+ for (r = &entry->payload[entry->length - 2]; r >= ent