summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGergely Nagy <algernon@balabit.hu>2012-08-13 23:05:50 +0200
committerGergely Nagy <algernon@balabit.hu>2012-08-13 23:05:50 +0200
commitaf1cbe2c1d4c3aa749b7f723e741a4a4df1a321a (patch)
treebfef4a37932b6f90441e4b55a3a7ea6d01e545c2
parent3c2213da88c3dfc957a8505f5addc5194901c92f (diff)
parent2b2c352e1ec33eaea956a2b039840686be14b32c (diff)
Merge tag 'libumberlog-0.3.0' into debian
libumberlog 0.3.0 release
-rw-r--r--.travis.yml6
-rw-r--r--NEWS71
-rw-r--r--README.rst19
-rw-r--r--TODO.org123
-rwxr-xr-xautogen.sh7
-rw-r--r--configure.ac21
-rw-r--r--lib/Makefile.am16
-rw-r--r--lib/buffer.c273
-rw-r--r--lib/buffer.h8
-rw-r--r--lib/libumberlog.ld9
-rw-r--r--lib/umberlog.c602
-rw-r--r--lib/umberlog.h14
-rw-r--r--lib/umberlog.rst82
-rw-r--r--lib/umberlog_preload.c64
-rw-r--r--t/Makefile.am16
-rw-r--r--t/test-common.c66
-rw-r--r--t/test-common.h14
-rw-r--r--t/test_perf.c14
-rw-r--r--t/test_umberlog.c314
-rw-r--r--t/test_umberlog_preload.c145
20 files changed, 1256 insertions, 628 deletions
diff --git a/.travis.yml b/.travis.yml
index db9129c..83cab25 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,12 @@
language: c
install: sudo apt-get install libjson0-dev check python-docutils
before_script:
- - ./autogen.sh
+ - install -d m4
+ - autoreconf -i
script:
- ./configure
- make
- make check
+compiler:
+ - gcc
+ - clang
diff --git a/NEWS b/NEWS
index 14311e0..b8c0e1a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,76 @@
#+STARTUP: indent showall -*- org -*-
+* 0.3.0 - <2012-08-13 Mon>
+
+This release is is heavily based on the work of Miloslav Trmač
+<mitr@redhat.com>, many thanks for his contributions!
+
+** API/ABI breaking changes
+
+*** The library was split into two variants
+
+There is now an LD_PRELOAD variant (libumberlog_preload.so), and a
+library applications can link to. The former has overrides for the
+syslog() family of functions, the latter does not.
+
+See the documentation on what other differences there are between the
+two!
+
+*** The LOG_UL_NODISCOVER flag was renamed to LOG_UL_NOIMPLICIT
+
+The term "discover" wasn't entirely clear, and did not correctly
+reflect what goes on anyway. For this reason, it was renamed to
+LOG_UL_NOIMPLICIT.
+
+*** New function: ul_set_log_flags()
+
+Instead of being able to set flags via (ul_)openlog(), all flags must
+be set from this version forward with ul_set_log_flags().
+
+The LD_PRELOAD variant will ship with all flags enabled by default, so
+that automatically discovered fields can be added implicitly.
+
+*** Settings and caches are now per-process, not per-thread
+
+Settings formerly set via ul_openlog() and cached values were
+thread-local before, they are now per-process, to mimic the original
+syslog() family more closely.
+
+*** Further syslog() compatibility enhancements
+
+The ul_closelog() function does not clear the previously set flags, to
+be in line with how the original closelog() works.
+
+** Features
+
+*** Performance improvements
+
+Significant performance improvements and internal code cleanups have
+been made, resulting in some cases over 20% speed ups.
+
+*** Support for a wider range of format strings
+
+On glibc-based platforms, the library now uses parse_printf_format(),
+to support a far wider range of format strings than the old code
+did. On other platforms, the old code is still used - but has received
+improvements too.
+
+** Bugfixes
+
+*** Fixed a 1-byte heap overflow in ul_buffer_initialize
+
+*** Don't use \f in JSON, use \uNNNN instead
+
+Some parsers - notably json-c - do not recognise \f, so fall back to
+\uNNNN instead.
+
+*** Timestamp formatting has been fixed
+
+*** Log mask handling has also been fixed
+
+Previously, if the mask was not a pure priority mask, the library did
+not handle it appropriately.
+
* 0.2.1 - <2012-05-02 Wed>
** Fixed JSON formatting for syslog() messages
diff --git a/README.rst b/README.rst
index 5e2b792..c6403c1 100644
--- a/README.rst
+++ b/README.rst
@@ -73,7 +73,7 @@ The library follows the usual autotools way of installation:
$ git clone git://github.com/algernon/libumberlog.git
$ cd libumberlog
- $ ./autogen.sh
+ $ autoreconf -i
$ ./configure && make && make install
.. image:: https://secure.travis-ci.org/algernon/libumberlog.png?branch=master
@@ -81,15 +81,16 @@ The library follows the usual autotools way of installation:
Usage
-----
-The library can either be used as an LD_PRELOAD-able shared object, in
-which case it overrides the system-supplied ``syslog()`` calls with
-its own, or as a proper library. In the latter case, please see the
-`API documentation`_ for more information.
+The library comes in two variants: one to link against, and provides
+an enhanced, ``syslog()``-like API. For this, please see the `API
+documentation`_ for more information.
-In the former case, using the library is as easy as setting
-**LD_PRELOAD** prior to executing a program (if one wants to control
-this on a per-program basis), or adding the path to the installed
-library to ``/etc/ld.so.preload``.
+The other variant is an LD_PRELOAD-able shared object, installed as
+``libumberlog_preload.so`` into a subdirectory of one's libdir. This
+one overrides the system-suploed ``syslog()`` calls with its own
+version, and turns these into CEE-emitting functions. It can be used
+either on a per-application basis, by setting **LD_PRELOAD**, or
+adding the path to the library to ``/etc/ld.so.preload``.
.. _API documentation: http://algernon.github.com/libumberlog/umberlog.html
diff --git a/TODO.org b/TODO.org
deleted file mode 100644
index 10c9b30..0000000
--- a/TODO.org
+++ /dev/null
@@ -1,123 +0,0 @@
-# -*- org -*-
-#+STARTUP: indent showall lognotedone
-#+TODO: TODO(t) WIP(p) MAYBE(m) | CANCELED(c) DONE(d)
-#+OPTIONS: tasks:todo
-
-* DONE Features
-CLOSED: [2012-03-22 Thu 18:05]
-- CLOSING NOTE [2012-03-22 Thu 18:05] \\
- With all the planned features done or cancelled, we're feature-complete!
-** DONE More autodiscovery
-CLOSED: [2012-03-22 Thu 18:03]
-- CLOSING NOTE [2012-03-22 Thu 18:03]
-*** DONE UID/GID discovery
-CLOSED: [2012-03-20 Tue 11:47]
-Cached by default, should be able to turn of caching with the global
-LOG_CEE_NOCACHE flag, and also with LOG_CEE_NOCACHE_UID flag.
-*** CANCELED Thread-id discovery
-CLOSED: [2012-03-22 Thu 18:02]
-- CLOSING NOTE [2012-03-22 Thu 18:02] \\
- Not going to do it, it's more trouble than it's worth. We not only
- have to figure out at compile time whether the platform supports
- gettid(), at run time, we also need to figure out whether the kernel
- supports it.. No thanks. Interested applications can just use
- cee_syslog() and add it themselves.
-Not cached by default, but can be turned on with
-LOG_CEE_CACHE_TID. This is linux-specific, and glibc does not provide
-a wrapper, so this also needs some configure-time discovery.
-*** DONE Timestamp in the payload
-CLOSED: [2012-03-21 Wed 14:20]
-- CLOSING NOTE [2012-03-21 Wed 14:20] \\
- Use clock_gettime(), and format the string with full precision.
-Not enabled by default, can be toggled on with LOG_CEE_TIMESTAMP.
-**** What precision do we want?
-*** DONE Hostname in the payload
-CLOSED: [2012-03-21 Wed 12:53]
-- CLOSING NOTE [2012-03-21 Wed 12:53] \\
- Done, we use gethostname(), as that's the easiest that yields somewhat
- useful results.
-Do we want it, at all? If so, cache gethostname() and be happy, or is
-there any other, better way?
-** DONE Formatting of discovered stuff
-CLOSED: [2012-03-22 Thu 18:05]
-- CLOSING NOTE [2012-03-22 Thu 18:05]
-*** CANCELED Timestamp as long/long pair instead of formatted string?
-CLOSED: [2012-03-22 Thu 18:03]
-- CLOSING NOTE [2012-03-22 Thu 18:03] \\
- The text format will do. It has the advantage of being a single value,
- easily parsable and so on and so forth. Adding ts_sec and ts_nsec
- would make less sense out of the box. People can do that by setting
- LOG_CEE_NOTIME, and adding the info themselves.
-The timestamp is formatted to a string now, which is a considerable
-effort, and will need to be parsed back again. Do we want this? Or
-should we add the timestamp as a ts_sec & ts_nsec pair?
-
-It's probably best to stay with the string, though.
-
-* DONE Documentation
-CLOSED: [2012-03-22 Thu 12:27]
-- CLOSING NOTE [2012-03-22 Thu 12:27]
-** DONE cee-syslog(3)
-CLOSED: [2012-03-22 Thu 12:27]
-- CLOSING NOTE [2012-03-22 Thu 12:27] \\
- Manual page is now complete.
-The main entry point of the documentation, will detail the purpose and
-goals of the library, along with the functions and constants it
-provides. cee_syslog(), cee_vsyslog(), cee_legacy_*(), cee_openlog()
-and cee_setlogmask() will be aliases to this page - similar how the
-syslog functions are documented.
-
-It makes much more sense to have a single page for them all.
-
-* DONE Test suite
-CLOSED: [2012-03-20 Tue 12:29]
-- CLOSING NOTE [2012-03-20 Tue 12:29] \\
- Test suite is in place.
-The test suite cannot test the syslog calls themselves, as that would
-be way too hacky to do properly. So it will test cee_format() instead,
-which is used by the syslog calls anyway.
-
-** DONE Decide how to test the library
-CLOSED: [2012-03-20 Tue 12:29]
-- CLOSING NOTE [2012-03-20 Tue 12:29] \\
- It's decided, I'll do just that.
-I believe the best way to test would be to re-parse the JSON, extract
-the fields, and verify they're of the expected value and type.
-
-* DONE Bugfixes
-CLOSED: [2012-04-08 Sun 10:59]
-** DONE Missing va_copy() when passing around varargs
-CLOSED: [2012-04-08 Sun 10:58]
-- CLOSING NOTE [2012-04-08 Sun 10:58] \\
- Ended up writing a format string parsing & spinning macro. This way we
- can keep the API and the ABI stable too at the cost of some
- performance (which isn't the strong point of the library anyway).
-The way we pass around varargs is unsafe, and causes crashes on a few
-systems where we would need to copy the thing around.
-
-We need a way to get the post-message key-value pairs some other way,
-or with using va_copy() and counting format strings and whatnot.
-* DONE Remove json-c dependency
-CLOSED: [2012-04-13 Fri 13:10]
-- CLOSING NOTE [2012-04-13 Fri 13:10] \\
- Implemented proper escaping too, and added a test case to verify the behaviour.
-- Note taken on [2012-04-13 Fri 12:09] \\
- Work in progress: the buffer handling was moved to buffer.c, it does
- the heavy lifting and JSON formatting. It does not do escaping yet,
- however.
-We use very little of json-c, all we do is append key-value pairs,
-where both keys and values are always strings. We pretty much only use
-json-c to escape these for us, if so need be.
-
-Instead of pulling in a library of which we only use a tiny subset of
-the features, we should implement our own escaping (UTF-8 supporting
-and all) and remove the json-c dependency. This will slightly increase
-our code size, but drastically reduce our compiled footprint.
-* TODO Examples
-While the test suite will provide some easier examples, it would be
-nice to have a bigger example program that show-cases all the function
-calls, with detailed documentation about the pitfalls, too.
-
-*** MAYBE Forked and threaded example?
-Ideally, this example would also fork and thread, just to be able to
-document how those things are affected.
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 40b6e0f..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /bin/sh
-
-libtoolize --force --copy --automake
-aclocal -I m4 --install
-autoheader
-automake --foreign --add-missing --copy
-autoconf
diff --git a/configure.ac b/configure.ac
index a3cf642..fb312bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,8 @@
dnl Process this file with autoconf to produce a configure script.
dnl
-AC_INIT(libumberlog, 0.2.1, algernon@balabit.hu, libumberlog, https://github.com/algernon/libumberlog)
-AM_INIT_AUTOMAKE([1.9 tar-ustar])
+AC_INIT(libumberlog, 0.3.0, algernon@balabit.hu, libumberlog, https://github.com/algernon/libumberlog)
+AM_INIT_AUTOMAKE([1.11 foreign tar-ustar no-dist-gzip dist-xz])
dnl ***************************************************************************
dnl dependencies
@@ -20,7 +20,6 @@ dnl ***************************************************************************
dnl Checks for programs.
AC_PROG_CC
AC_PROG_CXX
-AC_PROG_RANLIB
AM_PROG_CC_STDC
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
@@ -40,7 +39,9 @@ AC_CHECK_HEADERS([syslog.h dlfcn.h limits.h wchar.h])
dnl ***************************************************************************
dnl Checks for libraries
-AC_CHECK_FUNCS([gethostname strdup memset __syslog_chk])
+dnl Note that program_invocation_short_name is a variable, not a function; this
+dnl currently happens to work fine.
+AC_CHECK_FUNCS([gethostname strdup memset __syslog_chk parse_printf_format program_invocation_short_name])
dnl The dlopen() function is in the C library for *BSD and in
dnl libdl on GLIBC-based systems
@@ -72,6 +73,18 @@ AC_TYPE_PID_T
AC_TYPE_UID_T
AC_TYPE_SIZE_T
+AC_ARG_ENABLE([discovery],
+ AS_HELP_STRING([--disable-discovery],
+ [Do not implicitly add automatically discovered fields when using the LD_PRELOAD lib [default=enabled]]),
+ [], [enable_discovery=yes])
+if test "x$enable_discovery" != xno; then
+ DEFAULT_LOG_FLAGS=LOG_UL_ALL
+else
+ DEFAULT_LOG_FLAGS=LOG_UL_NOIMPLICIT
+fi
+AC_DEFINE_UNQUOTED([DEFAULT_LOG_FLAGS], [$DEFAULT_LOG_FLAGS],
+ [Default flags for the LD_PRELOAD variant of the library])
+
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [package name])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [version number])
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b6b60b3..19b7a65 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,12 +1,14 @@
-LUL_CURRENT = 2
+LUL_CURRENT = 3
LUL_REVISION = 0
-LUL_AGE = 1
+LUL_AGE = 0
lib_LTLIBRARIES = libumberlog.la
libumberlog_la_LDFLAGS = -Wl,--version-script,${srcdir}/libumberlog.ld \
-version-info ${LUL_CURRENT}:${LUL_REVISION}:${LUL_AGE}
+EXTRA_libumberlog_la_DEPENDENCIES = libumberlog.ld
libumberlog_la_SOURCES = umberlog.c umberlog.h buffer.c buffer.h
+libumberlog_la_LIBADD = -lpthread
libumberlog_includedir = $(includedir)
libumberlog_include_HEADERS = umberlog.h
@@ -14,8 +16,18 @@ libumberlog_include_HEADERS = umberlog.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libumberlog.pc
+pkglib_LTLIBRARIES = libumberlog_preload.la
+
+libumberlog_preload_la_SOURCES = umberlog_preload.c buffer.c buffer.h umberlog.h
+libumberlog_preload_la_LIBADD = -lpthread
+libumberlog_preload_la_LDFLAGS = -avoid-version -shared
+
EXTRA_DIST = umberlog.rst libumberlog.ld
+# We need this dependency, but can't compile umberlog.c into
+# libumberlog_preload.
+umberlog_preload.c: umberlog.c
+
if ENABLE_MANS
man3_MANS = umberlog.3
CLEANFILES = umberlog.3
diff --git a/lib/buffer.c b/lib/buffer.c
index ef448bf..79c18fd 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -30,208 +30,205 @@
#include "config.h"
#include "buffer.h"
+#include <limits.h>
#include <stdlib.h>
#include <string.h>
-static const unsigned char json_exceptions[] =
- {
- 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
- 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
- 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
- 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e,
- 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
- 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae,
- 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe,
- 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
- 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,
- 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
- 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
- 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
- 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee,
- 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
- 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
- 0xff, '\0'
- };
-
-static __thread ul_buffer_t escape_buffer;
-
-static void ul_buffer_finish (void) __attribute__((destructor));
-
-static void
-ul_buffer_finish (void)
+static int
+_ul_buffer_realloc_to_reserve (ul_buffer_t *buffer, size_t size)
{
- free (escape_buffer.msg);
+ size_t new_alloc, ptr_offset;
+ void *new_msg;
+
+ new_alloc = (buffer->alloc_end - buffer->msg + size) * 2;
+ ptr_offset = buffer->ptr - buffer->msg;
+ new_msg = realloc (buffer->msg, new_alloc);
+ if (new_msg == NULL)
+ return -1;
+ buffer->msg = new_msg;
+ buffer->ptr = new_msg + ptr_offset;
+ buffer->alloc_end = new_msg + new_alloc;
+ return 0;
}
-static inline char *
-_ul_str_escape (const char *str, char *dest, size_t *length)
+static inline int
+_ul_buffer_reserve_size (ul_buffer_t *buffer, size_t size)
{
+ if ((size_t)(buffer->alloc_end - buffer->ptr) < size)
+ return _ul_buffer_realloc_to_reserve (buffer, size);
+ return 0;
+}
+
+static inline int
+_ul_str_escape (ul_buffer_t *dest, const char *str)
+{
+ /* Assumes ASCII! Keep in sync with the switch! */
+ static const unsigned char json_exceptions[UCHAR_MAX + 1] =
+ {
+ [0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1, [0x05] = 1, [0x06] = 1,
+ [0x07] = 1, [0x08] = 1, [0x09] = 1, [0x0a] = 1, [0x0b] = 1, [0x0c] = 1,
+ [0x0d] = 1, [0x0e] = 1, [0x0f] = 1, [0x10] = 1, [0x11] = 1, [0x12] = 1,
+ [0x13] = 1, [0x14] = 1, [0x15] = 1, [0x16] = 1, [0x17] = 1, [0x18] = 1,
+ [0x19] = 1, [0x1a] = 1, [0x1b] = 1, [0x1c] = 1, [0x1d] = 1, [0x1e] = 1,
+ [0x1f] = 1, ['\\'] = 1, ['"'] = 1
+ };
+
const unsigned char *p;
- char *q;
- static unsigned char exmap[256];
- static int exmap_inited;
+ char *q, *end;
if (!str)
- return NULL;
+ return -1;
p = (unsigned char *)str;
- q = dest;
+ q = dest->ptr;
+ end = dest->alloc_end;
+
+#define BUFFER_RESERVE(SIZE) \
+ do \
+ { \
+ if (end - q < (SIZE)) \
+ { \
+ dest->ptr = q; \
+ if (_ul_buffer_reserve_size (dest, (SIZE)) != 0) \
+ return -1; \
+ q = dest->ptr; \
+ end = dest->alloc_end; \
+ } \
+ } \
+ while (0)
- if (!exmap_inited)
+ while (*p)
{
- const unsigned char *e = json_exceptions;
-
- memset (exmap, 0, 256);
- while (*e)
+ if (json_exceptions[*p] == 0)
{
- exmap[*e] = 1;
- e++;
+ /* This is a slightly faster variant of equivalent to
+ BUFFER_RESERVE (1) */
+ if (q == end)
+ {
+ dest->ptr = q;
+ if (_ul_buffer_reserve_size (dest, 1) != 0)
+ return -1;
+ q = dest->ptr;
+ end = dest->alloc_end;
+ }
+ *q++ = *p;
}
- exmap_inited = 1;
- }
-
- while (*p)
- {
- if (exmap[*p])
- *q++ = *p;
else
{
+ /* Keep in sync with json_exceptions! */
switch (*p)
{
case '\b':
- *q++ = '\\';
- *q++ = 'b';
- break;
- case '\f':
- *q++ = '\\';
- *q++ = 'f';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\b", 2);
+ q += 2;
break;
case '\n':
- *q++ = '\\';
- *q++ = 'n';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\n", 2);
+ q += 2;
break;
case '\r':
- *q++ = '\\';
- *q++ = 'r';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\r", 2);
+ q += 2;
break;
case '\t':
- *q++ = '\\';
- *q++ = 't';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\t", 2);
+ q += 2;
break;
case '\\':
- *q++ = '\\';
- *q++ = '\\';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\\\", 2);
+ q += 2;
break;
case '"':
- *q++ = '\\';
- *q++ = '"';
+ BUFFER_RESERVE (2);
+ memcpy (q, "\\\"", 2);
+ q += 2;
break;
default:
- if ((*p < ' ') || (*p >= 0177))
- {
- const char *json_hex_chars = "0123456789abcdef";
-
- *q++ = '\\';
- *q++ = 'u';
- *q++ = '0';
- *q++ = '0';
- *q++ = json_hex_chars[(*p) >> 4];
- *q++ = json_hex_chars[(*p) & 0xf];
- }
- else
- *q++ = *p;
- break;
+ {
+ static const char json_hex_chars[16] = "0123456789abcdef";
+
+ BUFFER_RESERVE (6);
+ *q++ = '\\';
+ *q++ = 'u';
+ *q++ = '0';
+ *q++ = '0';
+ *q++ = json_hex_chars[(*p) >> 4];
+ *q++ = json_hex_chars[(*p) & 0xf];
+ break;
+ }
}
}
p++;
}
+ dest->ptr = q;
- *q = 0;
- if (length)
- *length = q - dest;
- return dest;
+ return 0;
}
-static inline ul_buffer_t *
-_ul_buffer_ensure_size (ul_buffer_t *buffer, size_t size)
-{
- if (buffer->alloc < size)
- {
- buffer->alloc += size * 2;
- buffer->msg = realloc (buffer->msg, buffer->alloc);
- if (!buffer->msg)
- return NULL;
- }
- return buffer;
-}
-
-ul_buffer_t *
+int
ul_buffer_reset (ul_buffer_t *buffer)
{
- buffer->len = 1;
- _ul_buffer_ensure_size (buffer, 512);
- buffer->msg[0] = '{';
- return buffer;
+ buffer->ptr = buffer->msg;
+ if (_ul_buffer_reserve_size (buffer, 512) != 0)
+ return -1;
+ *buffer->ptr++ = '{';
+ return 0;
}
ul_buffer_t *
ul_buffer_append (ul_buffer_t *buffer, const char *key, const char *value)
{
- char *k, *v;
- size_t lk, lv;
- size_t orig_len = buffer->len;
+ size_t orig_len = buffer->ptr - buffer->msg;
/* Append the key to the buffer */
- escape_buffer.len = 0;
- _ul_buffer_ensure_size (&escape_buffer, strlen (key) * 6 + 1);
- k = _ul_str_escape (key, escape_buffer.msg, &lk);
- if (!k)
- return NULL;
+ if (_ul_buffer_reserve_size (buffer, 1) != 0)
+ goto err;
+ *buffer->ptr++ = '"';
- buffer = _ul_buffer_ensure_size (buffer, buffer->len + lk + 4);
- if (!buffer)
- return NULL;
+ if (_ul_str_escape (buffer, key) != 0)
+ goto err;
- memcpy (buffer->msg + buffer->len, "\"", 1);
- memcpy (buffer->msg + buffer->len + 1, k, lk);
- memcpy (buffer->msg + buffer->len + 1 + lk, "\":\"", 3);
+ if (_ul_buffer_reserve_size (buffer, 3) != 0)
+ goto err;
+ memcpy (buffer->ptr, "\":\"", 3);
+ buffer->ptr += 3;
/* Append the value to the buffer */
- escape_buffer.len = 0;
- _ul_buffer_ensure_size (&escape_buffer, strlen (value) * 6 + 1);
- v = _ul_str_escape (value, escape_buffer.msg, &lv);
- if (!v)
- {
- buffer->len = orig_len;
- return NULL;
- }
-
- buffer = _ul_buffer_ensure_size (buffer, buffer->len + lk + lv + 6);
- if (!buffer)
- {
- buffer->len = orig_len;
- return NULL;
- }
+ if (_ul_str_escape (buffer, value) != 0)
+ goto err;
- memcpy (buffer->msg + buffer->len + 1 + lk + 3, v, lv);
- memcpy (buffer->msg + buffer->len + 1 + lk + 3 + lv, "\",", 2);
- buffer->len += lk + lv + 6;
+ if (_ul_buffer_reserve_size (buffer, 2) != 0)
+ goto err;
+ memcpy (buffer->ptr, "\",", 2);
+ buffer->ptr += 2;
return buffer;
+
+ err:
+ buffer->ptr = buffer->msg + orig_len;
+ return NULL;
}
char *
ul_buffer_finalize (ul_buffer_t *buffer)
{
- if (buffer->msg[buffer->len - 1] == ',')
- buffer->msg[buffer->len - 1] = '}';
+ if (buffer->ptr[-1] == ',')
+ {
+ if (_ul_buffer_reserve_size (buffer, 1) != 0)
+ return NULL;
+ buffer->ptr[-1] = '}';
+ }
else
{
- if (!_ul_buffer_ensure_size (buffer, buffer->len + 1))
+ if (_ul_buffer_reserve_size (buffer, 2) != 0)
return NULL;
- buffer->msg[buffer->len++] = '}';
+ *buffer->ptr++ = '}';
}
- buffer->msg[buffer->len] = '\0';
+ *buffer->ptr++ = '\0';
return buffer->msg;
}
diff --git a/lib/buffer.h b/lib/buffer.h
index 0d412e8..c38699e 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -32,12 +32,12 @@
typedef struct
{
- size_t alloc;
- size_t len;
- char *msg;
+ char *msg; /* Buffer start */
+ char *ptr; /* Place to append new data */
+ char *alloc_end; /* After last allocated byte */
} ul_buffer_t;
-ul_buffer_t *ul_buffer_reset (ul_buffer_t *buffer)
+int ul_buffer_reset (ul_buffer_t *buffer)
__attribute__((visibility("hidden")));
ul_buffer_t *ul_buffer_append (ul_buffer_t *buffer,
const char *key, const char *value)
diff --git a/lib/libumberlog.ld b/lib/libumberlog.ld
index 225d51c..ddde9ae 100644
--- a/lib/libumberlog.ld
+++ b/lib/libumberlog.ld
@@ -1,4 +1,4 @@
-LIBUMBERLOG_0.1.0 {
+LIBUMBERLOG_0.3.0 {
global:
# Our own symbols
ul_format;
@@ -8,15 +8,12 @@ LIBUMBERLOG_0.1.0 {
ul_legacy_syslog;
ul_legacy_vsyslog;
ul_openlog;
+ ul_closelog;
ul_setlogmask;
+ ul_set_log_flags;
local:
# Inherited from elsewhere, but should not be exported
facilitynames;
prioritynames;
};
-
-LIBUMBERLOG_0.2.1 {
- global:
- ul_closelog;
-} LIBUMBERLOG_0.1.0;
diff --git a/lib/umberlog.c b/lib/umberlog.c
index e4eb5d3..e0ce093 100644
--- a/lib/umberlog.c
+++ b/lib/umberlog.c
@@ -28,11 +28,15 @@
#define _GNU_SOURCE 1
#define SYSLOG_NAMES 1
+#include "config.h"
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <dlfcn.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
@@ -40,32 +44,48 @@
#include <time.h>
#include <errno.h>
#include <wchar.h>
+#ifdef HAVE_PARSE_PRINTF_FORMAT
+#include <printf.h>
+#endif
-#include "config.h"
#include "umberlog.h"
#include "buffer.h"
-static void (*old_syslog) ();
-static void (*old_vsyslog) ();
-static void (*old_openlog) ();
-static void (*old_closelog) ();
-static int (*old_setlogmask) ();
+static void (*old_syslog) (int priority, const char *message, ...);
+static void (*old_vsyslog) (int priority, const char *message, va_list ap);
+static void (*old_openlog) (const char *ident, int option, int facility);
+static void (*old_closelog) (void);
static void ul_init (void) __attribute__((constructor));
static void ul_finish (void) __attribute__((destructor));
-static __thread struct
+static struct
{
- int mask;
+ /* The lock is used only to serialize writes; we assume that reads are safe
+ even when racing with writes, note that POSIX does not guarantee this (but
+ the BSD syslog does the same thing). */
+ pthread_mutex_t lock;
int flags;
-
int facility;
+ const char *ident;
+
+ /* Cached data.
+ -1 (or an empty string) means no value cached. */
pid_t pid;
uid_t uid;
gid_t gid;
- const char *ident;
char hostname[_POSIX_HOST_NAME_MAX + 1];
-} ul_sys_settings;
+} ul_process_data =
+ {
+ PTHREAD_MUTEX_INITIALIZER,
+#if __UL_PRELOAD__
+ DEFAULT_LOG_FLAGS,
+#else
+ LOG_UL_ALL,
+#endif
+ LOG_USER, NULL,
+ -1, (uid_t)-1, (gid_t)-1, { 0, }
+ };
static __thread ul_buffer_t ul_buffer;
static __thread int ul_recurse;
@@ -77,7 +97,6 @@ ul_init (void)
old_vsyslog = dlsym (RTLD_NEXT, "vsyslog");
old_openlog = dlsym (RTLD_NEXT, "openlog");
old_closelog = dlsym (RTLD_NEXT, "closelog");
- old_setlogmask = dlsym (RTLD_NEXT, "setlogmask");
}
static void
@@ -86,26 +105,75 @@ ul_finish (void)
free (ul_buffer.msg);
}
+/* Must be called with ul_process_data.lock held. */
+static void
+_ul_reset_caches_locked (void)
+{
+ /* If either NOIMPLICIT or NOCACHE is set, don't cache stuff we
+ won't use. */
+ if ((ul_process_data.flags & (LOG_UL_NOIMPLICIT | LOG_UL_NOCACHE)) != 0)
+ {
+ ul_process_data.pid = -1;
+ ul_process_data.gid = -1;
+ ul_process_data.uid = -1;
+ ul_process_data.hostname[0] = '\0';
+ pthread_mutex_unlock (&ul_process_data.lock);
+ return;
+ }
+
+ ul_process_data.pid = getpid();
+
+ /* UID/GID caching can be toggled separately, so check that too,
+ and set them to -1 if caching is disabled. */
+ if ((ul_process_data.flags & LOG_UL_NOCACHE_UID) != 0)
+ {
+ ul_process_data.gid = -1;
+ ul_process_data.uid = -1;
+ }
+ else
+ {
+ ul_process_data.gid = getgid ();
+ ul_process_data.uid = getuid ();
+ }
+
+ gethostname (ul_process_data.hostname, _POSIX_HOST_NAME_MAX);
+}
+
+void
+ul_set_log_flags (int flags)
+{
+ pthread_mutex_lock (&ul_process_data.lock);
+ ul_process_data.flags = flags;
+ _ul_reset_caches_locked ();
+ pthread_mutex_unlock (&ul_process_data.lock);
+}
+
void
ul_openlog (const char *ident, int option, int facility)
{
old_openlog (ident, option, facility);
- ul_sys_settings.mask = old_setlogmask (0);
- ul_sys_settings.flags = option;
- ul_sys_settings.facility = facility;
- ul_sys_settings.pid = getpid ();
- ul_sys_settings.gid = getgid ();
- ul_sys_settings.uid = getuid ();
- ul_sys_settings.ident = ident;
-
- gethostname (ul_sys_settings.hostname, _POSIX_HOST_NAME_MAX);
+
+ pthread_mutex_lock (&ul_process_data.lock);
+ ul_process_data.facility = facility;
+ ul_process_data.ident = ident;
+
+ _ul_reset_caches_locked ();
+
+ pthread_mutex_unlock (&ul_process_data.lock);
}
void
ul_closelog (void)
{
old_closelog ();
- memset (&ul_sys_settings, 0, sizeof (ul_sys_settings));
+
+ pthread_mutex_lock (&ul_process_data.lock);
+ ul_process_data.ident = NULL;
+ ul_process_data.pid = -1;
+ ul_process_data.gid = (gid_t)-1;
+ ul_process_data.uid = (uid_t)-1;
+ ul_process_data.hostname[0] = '\0';
+ pthread_mutex_unlock (&ul_process_data.lock);
}
/** HELPERS **/
@@ -116,7 +184,7 @@ _find_facility (int prio)
int fac = prio & LOG_FACMASK;
if (fac == 0)
- fac = ul_sys_settings.facility;
+ fac = ul_process_data.facility;
while (facilitynames[i].c_name != NULL &&
facilitynames[i].c_val != fac)
@@ -145,150 +213,314 @@ _find_prio (int prio)
static inline pid_t
_find_pid (void)
{
- if (ul_sys_settings.flags & LOG_UL_NOCACHE)
- return getpid ();
- else
- return ul_sys_settings.pid;
+ pid_t pid;
+
+ pid = ul_process_data.pid;
+ if (pid == -1)
+ pid = getpid ();
+ return pid;
}
static inline uid_t
_get_uid (void)
{
- if (ul_sys_settings.flags & LOG_UL_NOCACHE ||
- ul_sys_settings.flags & LOG_UL_NOCACHE_UID)
- return getuid ();
- else
- return ul_sys_settings.uid;
+ uid_t uid;
+
+ uid = ul_process_data.uid;
+ if (uid == (uid_t)-1)
+ uid = getuid ();
+ return uid;
}
-static inline uid_t
+static inline gid_t
_get_gid (void)
{
- if (ul_sys_settings.flags & LOG_UL_NOCACHE ||
- ul_sys_settings.flags & LOG_UL_NOCACHE_UID)
- return getgid ();
- else
- return ul_sys_settings.gid;
+ gid_t gid;
+
+ gid = ul_process_data.gid;
+ if (gid == (gid_t)-1)
+ gid = getgid ();
+ return gid;
}
static inline const char *
-_get_hostname (void)
+_get_hostname (char *hostname_buffer)
{
- if (ul_sys_settings.flags & LOG_UL_NOCACHE)
- gethostname (ul_sys_settings.hostname, _POSIX_HOST_NAME_MAX);
- return ul_sys_settings.hostname;
+ if (ul_process_data.hostname[0] != '\0')
+ return ul_process_data.hostname;
+
+ gethostname (hostname_buffer, _POSIX_HOST_NAME_MAX);
+ return hostname_buffer;
}
-#define _ul_va_spin(fmt,ap) \
- { \
- size_t i; \
- \
- for (i = 0; i < strlen (fmt); i++) \
- { \
- int eof = 0; \
- \
- if (fmt[i] != '%') \
- continue; \
- i++; \
- while (eof != 1) \
- { \
- switch (fmt[i]) \
- { \
- case 'd': \
- case 'i': \
- case 'o': \
- case 'u': \
- case 'x': \
- case 'X': \
- if (fmt[i - 1] == 'l') \
- { \
- if (i - 2 > 0 && fmt[i - 2] == 'l') \
- (void)va_arg (ap, long long int); \
- else \
- (void)va_arg (ap, long int); \
- } \
- else \
- (void)va_arg (ap, int); \
- eof = 1; \
- break; \
- case 'e': \
- case 'E': \
- case 'f': \
- case 'F': \
- case 'g': \
- case 'G': \
- case 'a': \
- case 'A': \
- if (fmt[i - 1] == 'L') \
- (void)va_arg (ap, long double); \
- else \
- (void)va_arg (ap, double); \
- eof = 1; \
- break; \
- case 'c': \
- if (fmt [i - 1] == 'l') \
- (void)va_arg (ap, wint_t); \
- else \
- (void)va_arg (ap, int); \
- eof = 1; \
- break; \
- case 's': \
- if (fmt [i - 1] == 'l') \
- (void)va_arg (ap, wchar_t *); \
- else \
- (void)va_arg (ap, char *); \
- eof = 1; \
- break; \
- case 'p': \
- (void)va_arg (ap, void *); \
- eof = 1; \
- break; \
- case '%': \
- eof = 1; \
- break; \
- default: \
- i++; \
- } \
- } \
- } \
- }
+static inline const char *
+_get_ident (void)
+{
+ const char *ident;
-static inline ul_buffer_t *
-_ul_json_vappend (ul_buffer_t *buffer, va_list ap)
+ ident = ul_process_data.ident;
+#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+ if (ident == NULL)
+ ident = program_invocation_short_name;
+#endif
+ return ident;
+}
+
+#ifdef HAVE_PARSE_PRINTF_FORMAT
+
+#define _ul_va_spin _ul_va_spin_glibc
+
+static int
+_ul_va_spin_glibc (const char *fmt, va_list *pap)
{
- char *key;
+ size_t num_args, i;
+ int *types;
- while ((key = (char *)va_arg (ap, char *)) != NULL)
- {
- char *fmt = (char *)va_arg (ap, char *);
- char *value = NULL;
- va_list aq;
+ num_args = parse_printf_format (fmt, 0, NULL);
+ types = malloc (num_args * sizeof (*types));
+ if (types == NULL)
+ goto err;
+ if (parse_printf_format (fmt, num_args, types) != num_args)
+ goto err; /* Should never happen */
- va_copy (aq, ap);
- if (vasprintf (&value, fmt, aq) == -1)
+ for (i = 0; i < num_args; i++)
+ {
+ switch (types[i])
{
- va_end (aq);
- return NULL;
+ case PA_CHAR:
+ case PA_INT | PA_FLAG_SHORT:
+ case PA_INT:
+ (void)va_arg (*pap, int);
+ break;
+ case PA_INT | PA_FLAG_LONG:
+ (void)va_arg (*pap, long int);
+ break;
+ case PA_INT | PA_FLAG_LONG_LONG:
+ (void)va_arg (*pap, long long int);
+ break;
+
+ case PA_WCHAR:
+ (void)va_arg (*pap, wint_t);
+ break;
+
+ case PA_STRING:
+ (void)va_arg (*pap, char *);
+ break;
+
+ case PA_WSTRING:
+ (void)va_arg (*pap, wchar_t *);
+ break;
+
+ case PA_POINTER:
+ (void)va_arg (*pap, void *);
+ break;
+
+ case PA_FLOAT:
+ case PA_DOUBLE:
+ (void)va_arg (*pap, double);
+ break;
+ case PA_DOUBLE | PA_FLAG_LONG_DOUBLE:
+ (void)va_arg (*pap, long double);
+ break;
+
+ default:
+ if ((types[i] & PA_FLAG_PTR) != 0)
+ {
+ (void)va_arg (*pap, void *);
+ break;
+ }
+ /* Unknown user-defined parameter type. Can we log that this
+ happened? */
+ goto err;
}
- va_end (aq);
+ }
- if (!value)
- return NULL;
+ free (types);
+ return 0;
- buffer = ul_buffer_append (buffer, key, value);
+ err:
+ free (types);
+ return -1;
+}
+#else /* !HAVE_PARSE_PRINTF_FORMAT */
- if (buffer == NULL)
+#define _ul_va_spin _ul_va_spin_legacy
+
+static int
+_ul_va_spin_legacy (const char *fmt, va_list *pap)
+{
+ size_t i;
+
+ for (i = 0; i < strlen (fmt); i++)
+ {
+ int eof = 0;
+
+ if (fmt[i] != '%')
+ continue;
+ i++;
+ while (eof != 1)
{
- free (value);
- return NULL;
+ switch (fmt[i])
+ {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (fmt[i - 1] == 'l')
+ {
+ if (i - 2 > 0 && fmt[i - 2] == 'l')
+ (void)va_arg (*pap, long long int);
+ else
+ (void)va_arg (*pap, long int);
+ }
+ else if (fmt[i - 1] == 'j')
+ (void)va_arg (*pap, intmax_t);
+ else if (fmt[i - 1] == 'z')
+ (void)va_arg (*pap, ssize_t);
+ else if (fmt[i - 1] == 't')
+ (void)va_arg (*pap, ptrdiff_t);
+ else /* Also handles h, hh */
+ (void)va_arg (*pap, int);
+ eof = 1;
+ break;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ case 'a':
+ case 'A':
+ if (fmt[i - 1] == 'L')
+ (void)va_arg (*pap, long double);
+ else
+ (void)va_arg (*pap, double);
+ eof = 1;
+ break;
+ case 'c':
+ if (fmt [i - 1] == 'l')
+ (void)va_arg (*pap, wint_t);
+ else
+ (void)va_arg (*pap, int);
+ eof = 1;
+ break;
+ case 'C':
+ (void)va_arg (*pap, wint_t);
+ eof = 1;
+ break;
+ case 's':
+ if (fmt [i - 1] == 'l')
+ (void)va_arg (*pap, wchar_t *);
+ else
+ (void)va_arg (*pap, char *);
+ eof = 1;
+ break;
+ case 'S':
+ (void)va_arg (*pap, wchar_t *);
+ eof = 1;
+ break;
+ case 'p':
+ (void)va_arg (*pap, void *);
+ eof = 1;
+ break;
+ case 'n':
+ if (fmt[i - 1] == 'l')
+ {
+ if (i - 2 > 0 && fmt[i - 2] == 'l')
+ (void)va_arg (*pap, long long *);
+ else
+ (void)va_arg (*pap, long int *);
+ }
+ else if (fmt[i - 1] == 'h')
+ {
+ if (i - 2 > 0 && fmt[i - 2] == 'h')
+ (void)va_arg (*pap, signed char *);
+ else
+ (void)va_arg (*pap, short int *);
+ }
+ else if (fmt[i - 1] == 'j')
+ (void)va_arg (*pap, intmax_t *);
+ else if (fmt[i - 1] == 'z')
+ (void)va_arg (*pap, ssize_t *);
+ else if (fmt[i - 1] == 't')
+ (void)va_arg (*pap, ptrdiff_t *);
+ else
+ (void)va_arg (*pap, int *);
+ eof = 1;
+ break;
+ case '*':
+ (void)va_arg (*pap, int);
+ i++;
+ break; /* eof stays set to 0 */
+ case '%':
+ eof = 1;
+ break;
+ default:
+ i++;
+ }
}
+ }
+ return 0;
+}
+#endif /* !HAVE_PARSE_PRINTF_FORMAT */
+
+/* Return a newly allocated string.
+ On failure, NULL is returned and it is undefined what PAP points to. */
+static char *
+_ul_vasprintf_and_advance (const char *fmt, va_list *pap)
+{
+ va_list aq;
+ char *res;
+ va_copy (aq, *pap);
+ if (vasprintf (&res, fmt, aq) < 0)
+ {
+ va_end (aq);
+ return NULL;
+ }
+ va_end (aq);
+ if (res == NULL)
+ return NULL;
+
+ if (_ul_va_spin (fmt, pap) != 0)
+ {
+ free (res);
+ return NULL;
+ }
+ return res;
+}
+
+static inline ul_buffer_t *
+_ul_json_vappend (ul_buffer_t *buffer, va_list ap_orig)
+{
+ va_list ap;
+ char *key;
+
+ /* "&ap" may not be possible for function parameters, so make a copy. */
+ va_copy (ap, ap_orig);
+ while ((key = (char *)va_arg (ap, char *)) != NULL)
+ {
+ char *fmt = (char *)va_arg (ap, char *);
+ char *value;
+
+ value = _ul_vasprintf_and_advance (fmt, &ap);
+ if (!value)
+ goto err;
+ buffer = ul_buffer_append (buffer, key, value);
free (value);
- _ul_va_spin (fmt, ap);
+ if (buffer == NULL)
+ goto err;
}
+ va_end (ap);
return buffer;
+
+ err:
+ va_end (ap);
+ return NULL;
}
static inline ul_buffer_t *
@@ -317,7 +549,7 @@ _ul_json_append_timestamp (ul_buffer_t *buffer)
strftime (stamp, sizeof (stamp), "%FT%T", tm);
strftime (zone, sizeof (zone), "%z", tm);
- return _ul_json_append (buffer, "timestamp", "%s.%lu%s",
+ return _ul_json_append (buffer, "timestamp", "%s.%09lu%s",
stamp, ts.tv_nsec, zone,
NULL);
}
@@ -325,20 +557,28 @@ _ul_json_append_timestamp (ul_buffer_t *buffer)
static inline ul_buffer_t *
_ul_discover (ul_buffer_t *buffer, int priority)
{
- if (ul_sys_settings.flags & LOG_UL_NODISCOVER)
+ char hostname_buffer[_POSIX_HOST_NAME_MAX + 1];
+ const char *ident;
+
+ if (ul_process_data.flags & LOG_UL_NOIMPLICIT)
return buffer;
buffer = _ul_json_append (buffer,
"pid", "%d", _find_pid (),
"facility", "%s", _find_facility (priority),
"priority", "%s", _find_prio (priority),
- "program", "%s", ul_sys_settings.ident,
"uid", "%d", _get_uid (),
"gid", "%d", _get_gid (),
- "host", "%s", _get_hostname (),
+ "host", "%s", _get_hostname (hostname_buffer),
NULL);
+ if (buffer == NULL)
+ return buffer;
+
+ ident = _get_ident ();
+ if (ident != NULL)
+ buffer = _ul_json_append (buffer, "program", "%s", ident, NULL);
- if (ul_sys_settings.flags & LOG_UL_NOTIME || !buffer)
+ if (ul_process_data.flags & LOG_UL_NOTIME || !buffer)
return buffer;
return _ul_json_append_timestamp (buffer);
@@ -347,41 +587,37 @@ _ul_discover (ul_buffer_t *buffer, int priority)
static inline ul_buffer_t *
_ul_vformat (ul_buffer_t *buffer, int format_version,
int priority, const char *msg_format,
- va_list ap)
+ va_list ap_orig)
{
char *value;
- va_list aq;
-
- va_copy (aq, ap);
- if (vasprintf (&value, msg_format, aq) == -1)
- {
- va_end (aq);
- return NULL;
- }
- va_end (aq);
- if (!value)
- return NULL;
+ va_list ap;
- ul_buffer_reset (buffer);
+ /* "&ap" may not be possible for function parameters, so make a copy. */
+ va_copy (ap, ap_orig);
+ if (ul_buffer_reset (buffer) != 0)
+ goto err;
+ value = _ul_vasprintf_and_advance (msg_format, &ap);
+ if (!value)
+ goto err;
buffer = ul_buffer_append (buffer, "msg", value);
- if (buffer == NULL)
- {
- free (value);
- return NULL;
- }
-
free (value);
- _ul_va_spin (msg_format, ap);
+ if (buffer == NULL)
+ goto err;
if (format_version > 0)
buffer = _ul_json_vappend (buffer, ap);
if (!buffer)
- return NULL;
+ goto err;
+ va_end (ap);
return _ul_discover (buffer, priority);
+
+ err:
+ va_end (ap);
+ return NULL;
}
static inline const char *
@@ -433,10 +669,9 @@ static inline int
_ul_vsyslog (int format_version, int priority,
const char *msg_format, va_list ap)
{
- const char *msg;
ul_buffer_t *buffer = &ul_buffer;
- if (!(ul_sys_settings.mask & priority))
+ if (!(setlogmask (0) & LOG_MASK (LOG_PRI (priority))))
return 0;
buffer = _ul_vformat (buffer, format_version, priority, msg_format, ap);
@@ -478,8 +713,8 @@ ul_legacy_vsyslog (int priority, const char *msg_format, va_list ap)
{
ul_recurse = 1;
_ul_vsyslog (0, priority, msg_format, ap);
+ ul_recurse = 0;
}
- ul_recurse = 0;
}
void
@@ -495,42 +730,5 @@ ul_legacy_syslog (int priority, const char *msg_format, ...)
int
ul_setlogmask (int mask)
{
- if (mask != 0)
- ul_sys_settings.mask = mask;
- return old_setlogmask (mask);
-}
-
-#if HAVE___SYSLOG_CHK
-void
-__syslog_chk (int __pri, int __flag, __const char *__fmt, ...)
-{
- va_list ap;
-
- va_start (ap, __fmt);
- ul_legacy_vsyslog (__pri, __fmt, ap);
- va_end (ap);
-}
-
-void
-__vsyslog_chk (int __pri, int __flag, __const char *__fmt, va_list ap)
-{
- ul_legacy_vsyslog (__pri, __fmt, ap);
+ return setlogmask (mask);
}
-#endif
-
-void openlog (const char *ident, int option, int facility)
- __attribute__((alias ("ul_openlog")));
-
-void closelog (void)
- __attribute__((alias ("ul_closelog")));
-
-#undef syslog
-void syslog (int priority, const char *msg_format, ...)
- __attribute__((alias ("ul_legacy_syslog")));
-
-#undef vsyslog
-void vsyslog (int priority, const char *msg_format, va_list ap)
- __attribute__((alias ("ul_legacy_vsyslog")));
-
-int setlogmask (int mask)
- __attribute__((alias ("ul_setlogmask")));
diff --git a/lib/umberlog.h b/lib/umberlog.h
index 51ed7be..2a0d08b 100644
--- a/lib/umberlog.h
+++ b/lib/umberlog.h
@@ -31,23 +31,25 @@
#include <syslog.h>
#include <stdarg.h>
-#define LOG_UL_NODISCOVER 0x0040
+#define LOG_UL_ALL 0x0000
+#define LOG_UL_NOIMPLICIT 0x0040
#define LOG_UL_NOCACHE 0x0080
#define LOG_UL_NOCACHE_UID 0x0100
#define LOG_UL_NOTIME 0x0200
char *ul_format (int priority, const char *msg_format, ...)
- __attribute__((sentinel));
-char *ul_vformat (int priority, const char *msg_format, va_list ap);
+ __attribute__((warn_unused_result, sentinel));
+char *ul_vformat (int priority, const char *msg_format, va_list ap)
+ __attribute__((warn_unused_result));
void ul_openlog (const char *ident, int option, int facility);
+void ul_set_log_flags (int flags);
void ul_closelog (void);
int ul_setlogmask (int mask);
int ul_syslog (int priority, const char *msg_format, ...)
- __attribute__((warn_unused_result, sentinel));
-int ul_vsyslog (int priority, const char *msg_format, va_list ap)
- __attribute__((warn_unused_result));
+ __attribute__((sentinel));
+int ul_vsyslog (int priority, const char *msg_format, va_list ap);
void ul_legacy_syslog (int priority, const char *msg_format, ...);
void ul_legacy_vsyslog (int priority, const char *msg_format, va_list ap);
diff --git a/lib/umberlog.rst b/lib/umberlog.rst
index d2cd354..65c824b 100644
--- a/lib/umberlog.rst
+++ b/lib/umberlog.rst
@@ -7,7 +7,7 @@ CEE-enhanced syslog message generation
--------------------------------------
:Author: Gergely Nagy <algernon@balabit.hu>
-:Date: 2012-04-28
+:Date: 2012-08-11
:Manual section: 3
:Manual group: CEE-enhanced syslog Manual
@@ -19,6 +19,7 @@ SYNOPSIS
#include <umberlog.h>
void ul_openlog (const char *ident, int option, int facility);
+ void ul_set_log_flags (int flags);
void ul_closelog (void);
int ul_syslog (int priority, const char *format, ....);
@@ -33,21 +34,20 @@ SYNOPSIS
DESCRIPTION
===========
-**ul_openlog()** (also aliased to **openlog()**) is a wrapper around
-the original **openlog()** function, which opens a connection to the
-system logger for a program. The updated version adds support for a
-number of new option flags, described below.
+**ul_openlog()** is a wrapper around the original **openlog()**
+function, which opens a connection to the system logger for a
+program.
-**ul_closelog()** (also aliased to **closelog()**) is similar to
-**ul_openlog()** in that it is a wrapper around the original
-**closelog()**. It clears any settings set so far, to get back to a
-clean state.
+**ul_set_log_flags** is to be used to set any combination of the new
+log flags described below.
+
+**ul_closelog()** is similar to **ul_openlog()** in that it is a
+wrapper around the original **closelog()**.
**ul_legacy_syslog()** and **ul_legacy_vsyslog()** are both thin
-layers over the original **syslog()** and **vsyslog()** functions, and
-the library overrides the original functions with this two. The only
-change these functions bring, are that the message they generate will
-be a CEE-enhanced message, with a JSON payload. See below for an
+layers over the original **syslog()** and **vsyslog()** functions. The
+only change these functions bring, are that the message they generate
+will be a CEE-enhanced message, with a JSON payload. See below for an
explanation on what this means.
**ul_syslog()** and **ul_vsyslog()** are two new functions provided by
@@ -59,10 +59,18 @@ other parameters it refers to, there must be a NULL-terminated list of
constructed from the *key* and the **printf(3)**-style *value format*
will be added to the generated message.
+Note that user-defined printf types defined by
+**register_printf_type()** are not supported.
+
**ul_format()** and **ul_vformat()** do the same as the syslog
variants above, except the formatted payload is not sent to syslog,
but returned as a newly allocated string.
+In the *LD_PRELOAD* variant of the library, **ul_openlog()** and
+**ul_closelog()** override the system-default **openlog()** and
+**closelog()** respectively, while **ul_legacy_syslog()** and
+**ul_legacy_vsyslog()** override **syslog()** and **vsyslog()**.
+
RETURN VALUE
============
@@ -80,15 +88,12 @@ ones and the new ones too turn the original syslog message into a
CEE-enabled JSON payload, with the original message put into the *msg*
field, and any additional fields put into the same structure.
-By default, unless the **LOG_UL_NODISCOVER** option flag is set, all
+By default, unless the **LOG_UL_NOIMPLICIT** option flag is set, all
of these functions will also add a few automatically discovered fields
into the payload:
*pid*
- The process ID of the program, as returned by **getpid()** The value
- of this is - by default - determined at the time of calling
- **ul_openlog()**, but if caching is disabled, it will be rechecked
- every time.
+ The process ID of the program, as returned by **getpid()**.
*facility*, *priority*
The syslog facility and priority as a text string.
@@ -97,40 +102,49 @@ into the payload:
The identification set at the time of **ul_openlog()**.
*uid*, *gid*
- The user and group ID of the process, determined at **ul_openlog()**
- time by default, unless caching is disabled.
+ The user and group ID of the process.
*host*
- The name of the originating host, determined at **ul_openlog()**
- time by default, using **gethostname()**.
+ The name of the originating host.
*timestamp*
+
High-precision timestamp, in textual format. Included by default,
- but can be controlled by the **LOG_UL_NOTIME** option flag at
- **ul_openlog()** time.
+ but can be disabled by adding the **LOG_UL_NOTIME** flag to a
+ **ul_set_log_flags()** call.
+If caching is disabled (see **LOG_UL_NOCACHE** below), the *pid*,
+*uid*, *gid*, and *host* fields will be regenerated for every log
+message, otherwise they're cached for as long as possible.
+
EXTRA OPTION FLAGS
==================
-The *option* argument to **ul_openlog()** is an OR of any of the
-original **openlog()** flags, and these:
+The *flag* argument to **ul_set_log_flags()** is an OR of any of these
+flags:
+
+LOG_UL_ALL
+ Enable all automatically discovered fields. This is the default
+ setting for the linkable library, but for the LD_PRELOAD variant, it
+ can be changed at the library's configure time.
-LOG_UL_NODISCOVER
- Disable all automatic discovery, and only include the *message*,
- and any specified *key-value* pairs in the generated message.
+LOG_UL_NOIMPLICIT
+ Disable adding any automatic discovered fields implicitly, and only
+ include the *message*, and any specified *key-value* pairs in the
+ generated message.
LOG_UL_NOCACHE
- When automatic discovery is enabled, disable caching certain
- properties, that might change between the call to **openlog()** and
- the **ul_syslog()** invocation.
+ When implict fields are enabled, disable caching certain properties,
+ that might change between the call to **openlog()** and the
+ **ul_syslog()** invocation.
LOG_UL_NOCACHE_UID
- Disable the *uid* and *gid* caching when automatic discovery is
+ Disable the *uid* and *gid* caching when implicit fields are
enabled, but do cache the rest.
LOG_UL_NOTIME
Do not add a high-precision timestamp to the generated message when
- automatic discovery is enabled.
+ implicit fields are enabled.
EXAMPLES
========
diff --git a/lib/umberlog_preload.c b/lib/umberlog_preload.c
new file mode 100644
index 0000000..eb5a8d7
--- /dev/null
+++ b/lib/umberlog_preload.c
@@ -0,0 +1,64 @@
+/* umberlog_preload.c -- CEE-enhanced syslog API, LD_PRELOAD version
+ *
+ * Copyright (c) 2012 BalaBit IT Security Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BALABIT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BALABIT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE 1
+
+#define __UL_PRELOAD__ 1
+
+#include "umberlog.c"
+
+#if HAVE___SYSLOG_CHK
+void
+__syslog_chk (int __pri, int __flag, __const char *__fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, __fmt);
+ ul_legacy_vsyslog (__pri, __fmt, ap);
+ va_end (ap);
+}
+
+void
+__vsyslog_chk (int __pri, int __flag, __const char *__fmt, va_list ap)
+{
+ ul_legacy_vsyslog (__pri, __fmt, ap);
+}
+#endif
+
+void openlog (const char *ident, int option, int facility)
+ __attribute__((alias ("ul_openlog")));
+
+void closelog (void)
+ __attribute__((alias ("ul_closelog")));
+
+#undef syslog
+void syslog (int priority, const char *msg_format, ...)
+ __attribute__((alias ("ul_legacy_syslog")));
+
+#undef vsyslog
+void vsyslog (int priority, const char *msg_format, va_list ap)
+ __attribute__((alias ("ul_legacy_vsyslog")));
diff --git a/t/Makefile.am b/t/Makefile.am
index 91e4b53..3f3a973 100644
--- a/t/Makefile.am
+++ b/t/Makefile.am
@@ -1,7 +1,15 @@
if ENABLE_TESTS
-TESTS = test_umberlog test_perf
-check_PROGRAMS = ${TESTS}
+TESTS = test_umberlog_preload test_umberlog test_perf
+check_PROGRAMS = ${TESTS}
-AM_CFLAGS = -I$(top_srcdir)/lib @JSON_CFLAGS@ @CHECK_CFLAGS@
-LDADD = $(top_builddir)/lib/libumberlog.la @JSON_LIBS@ @CHECK_LIBS@
+AM_CFLAGS = -I$(top_srcdir)/lib @JSON_CFLAGS@ @CHECK_CFLAGS@
+AM_LDFLAGS = -no-install
+LDADD = @JSON_LIBS@ @CHECK_LIBS@
+
+check_LTLIBRARIES = libultest.la
+libultest_la_SOURCES = test-common.c test-common.h
+
+test_umberlog_preload_LDADD = ${LDADD} $(top_builddir)/lib/libumberlog_preload.la libultest.la
+test_umberlog_LDADD = ${LDADD} $(top_builddir)/lib/libumberlog.la libultest.la
+test_perf_LDADD = ${LDADD} $(top_builddir)/lib/libumberlog.la libultest.la
endif
diff --git a/t/test-common.c b/t/test-common.c
new file mode 100644
index 0000000..707a5c7
--- /dev/null
+++ b/t/test-common.c
@@ -0,0 +1,66 @@
+#define _GNU_SOURCE 1
+
+#include <json.h>
+#include <assert.h>
+#include <check.h>
+
+void
+verify_value (struct json_object *jo, const char *key,
+ const char *expected_value)
+{
+ struct json_object *o;
+ const char *value;
+
+ o = json_object_object_get (jo, key);
+
+ ck_assert (o != NULL);
+
+ value = json_object_get_string (o);
+
+ ck_assert_str_eq (value, expected_value);
+}
+
+void
+verify_value_differs (struct json_object *jo, const char *key,
+ const char *unexpected_value)
+{
+ struct json_object *o;
+ const char *value;
+
+ o = json_object_object_get (jo, key);
+
+ ck_assert (o != NULL);
+
+ value = json_object_get_string (o);
+
+ ck_assert_str_ne (value, unexpected_value);
+}
+
+void
+verify_value_exists (struct json_object *jo, const char *key)
+{
+ struct json_object *o;
+
+ o = json_object_object_get (jo, key);
+ ck_assert_msg (o != NULL, "key '%s' does not exist", key);
+}
+
+void
+verify_value_missing (struct json_object *jo, const char *key)
+{
+ struct json_object *o;
+
+ o = json_object_object_get (jo, key);
+ assert (o == NULL);
+}
+
+struct json_object *
+parse_msg (const char *msg)
+{
+ struct json_object *jo;
+
+ jo = json_tokener_parse (msg);
+ assert (jo != NULL);
+
+ return jo;
+}
diff --git a/t/test-common.h b/t/test-common.h
new file mode 100644
index 0000000..dcf6f64
--- /dev/null
+++ b/t/test-common.h
@@ -0,0 +1,14 @@
+#ifndef UMBERLOG_TEST_COMMON_H
+#define UMBERLOG_TEST_COMMON_H 1
+
+#include <json.h>
+
+void verify_value (struct json_object *jo, const char *key,
+ const char *expected_value);
+void verify_value_differs (struct json_object *jo, const char *key,
+ const char *unexpected_value);
+void verify_value_exists (struct json_object *jo, const char *key);
+void verify_value_missing (struct json_object *jo, const char *key);
+
+struct json_object *parse_msg (const char *msg);
+#endif
diff --git a/t/test_perf.c b/t/test_perf.c
index 9e28b20..c729c4b 100644
--- a/t/test_perf.c
+++ b/t/test_perf.c
@@ -28,10 +28,10 @@ test_perf_simple (int flags, unsigned long cnt)
char *msg;
unsigned long i;
struct timespec st, et, dt;
- long nsec;
const char *fls;
- openlog ("umberlog/test_perf_simple", flags, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_perf_simple", 0, LOG_LOCAL0);
+ ul_set_log_flags (flags);
clock_gettime (CLOCK_MONOTONIC, &st);
for (i = 0; i < cnt; i++)
@@ -41,12 +41,12 @@ test_perf_simple (int flags, unsigned long cnt)
}
clock_gettime (CLOCK_MONOTONIC, &et);
- closelog ();
+ ul_closelog ();
dt = ts_diff (st, et);
- if (flags & LOG_UL_NODISCOVER)
- fls = "no-discover";
+ if (flags & LOG_UL_NOIMPLICIT)
+ fls = "no-implicit";
else if (flags & LOG_UL_NOTIME)
fls = "no-time";
else
@@ -63,8 +63,8 @@ main (void)
test_perf_simple (0, 100000);
test_perf_simple (0, 1000000);
- test_perf_simple (LOG_UL_NODISCOVER, 100000);
- test_perf_simple (LOG_UL_NODISCOVER, 1000000);
+ test_perf_simple (LOG_UL_NOIMPLICIT, 100000);
+ test_perf_simple (LOG_UL_NOIMPLICIT, 1000000);
test_perf_simple (LOG_UL_NOTIME, 100000);
test_perf_simple (LOG_UL_NOTIME, 1000000);
diff --git a/t/test_umberlog.c b/t/test_umberlog.c
index 9818a35..9184b35 100644
--- a/t/test_umberlog.c
+++ b/t/test_umberlog.c
@@ -1,6 +1,9 @@
#define _GNU_SOURCE 1
#include "umberlog.h"
+#include "config.h"
+#include "test-common.h"
+
#include <json.h>
#include <assert.h>
#include <string.h>
@@ -12,58 +15,89 @@
#include <check.h>
-static void
-verify_value (struct json_object *jo, const char *key,
- const char *expected_value)
+/**
+ * Test that openlog() is not overridden when using the linkable
+ * library. This must be the first test!
+ */
+START_TEST (test_overrides)
{
- struct json_object *o;
- const char *value;
+ char *msg;
+ struct json_object *jo;
+ char host[_POSIX_HOST_NAME_MAX + 1];
- o = json_object_object_get (jo, key);
+ openlog ("umberlog/test_overrides", 0, LOG_LOCAL0);
- ck_assert (o != NULL);
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
- value = json_object_get_string (o);
+ gethostname (host, _POSIX_HOST_NAME_MAX);
- ck_assert_str_eq (value, expected_value);
-}
+ verify_value (jo, "msg", "hello, I'm test_overrides!");
+ /* Default facility is user, and since we did not catch openlog, it
+ should not change. */
+ verify_value (jo, "facility", "user");
+ verify_value (jo, "priority", "debug");
+ /* The program is also caught by openlog(), so we'll get the
+ default back, unless we use ul_openlog(). */
+ verify_value (jo, "program", "test_umberlog");
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_exists (jo, "timestamp");
+ verify_value (jo, "host", host);
-static void
-verify_value_exists (struct json_object *jo, const char *key)
-{
- struct json_object *o;
+ json_object_put (jo);
- o = json_object_object_get (jo, key);
- ck_assert_msg (o != NULL, "key '%s' does not exist", key);
+ closelog ();
}
+END_TEST
-static void
-verify_value_missing (struct json_object *jo, const char *key)
+/**
+ * Test the umberlog defaults.
+ */
+START_TEST (test_defaults)
{
- struct json_object *o;
+ char *msg;
+ struct json_object *jo;
- o = json_object_object_get (jo, key);
- assert (o == NULL);
-}
+ char host[_POSIX_HOST_NAME_MAX + 1];
-static struct json_object *
-parse_msg (const char *msg)
-{
- struct json_object *jo;
+ ul_openlog ("umberlog/test_defaults", 0, LOG_LOCAL0);
+
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ gethostname (host, _POSIX_HOST_NAME_MAX);
+
+ verify_value (jo, "msg", "hello, I'm test_defaults!");
+ verify_value (jo, "facility", "local0");
+ verify_value (jo, "priority", "debug");
+ verify_value (jo, "program", "umberlog/test_defaults");
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_exists (jo, "timestamp");
+ verify_value (jo, "host", host);
- jo = json_tokener_parse (msg);
- assert (jo != NULL);
+ json_object_put (jo);
- return jo;
+ ul_closelog ();
}
+END_TEST
-START_TEST (test_simple)
+/**
+ * Test that using ul_openlog() does work, and sets up the program
+ * identity appropriately.
+ */
+START_TEST (test_ul_openlog)
{
char *msg;
struct json_object *jo;
char host[_POSIX_HOST_NAME_MAX + 1];
- openlog ("umberlog/test_simple", 0, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_ul_openlog", 0, LOG_LOCAL0);
msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
jo = parse_msg (msg);
@@ -71,10 +105,10 @@ START_TEST (test_simple)
gethostname (host, _POSIX_HOST_NAME_MAX);
- verify_value (jo, "msg", "hello, I'm test_simple!");
+ verify_value (jo, "msg", "hello, I'm test_ul_openlog!");
verify_value (jo, "facility", "local0");
verify_value (jo, "priority", "debug");
- verify_value (jo, "program", "umberlog/test_simple");
+ verify_value (jo, "program", "umberlog/test_ul_openlog");
verify_value_exists (jo, "pid");
verify_value_exists (jo, "uid");
verify_value_exists (jo, "gid");
@@ -83,22 +117,53 @@ START_TEST (test_simple)
json_object_put (jo);
- closelog ();
+ ul_closelog ();
+}
+END_TEST
+
+/**
+ * Test that ul_openlog() will ignore any LOG_UL_* flags.
+ */
+START_TEST (test_ul_openlog_flag_ignore)
+{
+ char *msg;
+ struct json_object *jo;
+
+ ul_openlog ("umberlog/test_ul_openlog_flag_ignore", LOG_UL_NOIMPLICIT,
+ LOG_LOCAL0);
+
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_exists (jo, "host");
+
+ json_object_put (jo);
+
+ ul_closelog ();
}
END_TEST
-START_TEST (test_no_discover)
+/**
+ * Test that setting LOG_UL_NOIMPLICIT will, indeed, turn off
+ * automatically discovered fields.
+ */
+START_TEST (test_no_implicit)
{
char *msg;
struct json_object *jo;
- openlog ("umberlog/test_no_discover", LOG_UL_NODISCOVER, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_no_implicit", 0, LOG_LOCAL0);
+ ul_set_log_flags (LOG_UL_NOIMPLICIT);
msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
jo = parse_msg (msg);
free (msg);
- verify_value (jo, "msg", "hello, I'm test_no_discover!");
+ verify_value (jo, "msg", "hello, I'm test_no_implicit!");
verify_value_missing (jo, "facility");
verify_value_missing (jo, "priority");
verify_value_missing (jo, "program");
@@ -110,16 +175,89 @@ START_TEST (test_no_discover)
json_object_put (jo);
- closelog ();
+ ul_closelog ();
}
END_TEST
+/**
+ * Test turning off the timestamp.
+ */
+START_TEST (test_no_timestamp)
+{
+ char *msg;
+ struct json_object *jo;
+
+ ul_openlog ("umberlog/test_no_timestamp", 0, LOG_LOCAL0);
+ ul_set_log_flags (LOG_UL_NOTIME);
+
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ verify_value (jo, "msg", "hello, I'm test_no_timestamp!");
+ verify_value (jo, "facility", "local0");
+ verify_value (jo, "priority", "debug");
+ verify_value (jo, "program", "umberlog/test_no_timestamp");
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_missing (jo, "timestamp");
+ verify_value_exists (jo, "host");
+
+ json_object_put (jo);
+
+ ul_closelog ();
+}
+END_TEST
+
+/**
+ * Test that closelog() does not clear the previous flag settings.
+ */
+START_TEST (test_closelog)
+{
+ char *msg;
+ struct json_object *jo;
+
+ ul_openlog ("umberlog/test_closelog", 0, LOG_LOCAL0);
+ ul_set_log_flags (LOG_UL_NOIMPLICIT);
+ ul_closelog ();
+
+ msg = ul_format (LOG_DEBUG, "%s", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ verify_value_missing (jo, "facility");
+
+ json_object_put (jo);
+
+ ul_openlog ("umberlog/test_closelog", 0, LOG_LOCAL0);
+ ul_closelog ();
+
+ msg = ul_format (LOG_DEBUG, "%s", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ verify_value_missing (jo, "facility");
+ verify_value_missing (jo, "program");
+
+ verify_value_missing (jo, "pid");
+ verify_value_missing (jo, "uid");
+ verify_value_missing (jo, "gid");
+ verify_value_missing (jo, "host");
+
+ json_object_put (jo);
+}
+END_TEST
+
+/**
+ * Test adding additional fields.
+ */
START_TEST (test_additional_fields)
{
char *msg;
struct json_object *jo;
- openlog ("umberlog/test_additional_fields", 0, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_additional_fields", 0, LOG_LOCAL0);
msg = ul_format (LOG_DEBUG, "testing 1, 2, 3...",
"random_number", "%d", 42,
@@ -134,16 +272,21 @@ START_TEST (test_additional_fields)
json_object_put (jo);
- closelog ();
+ ul_closelog ();
}
END_TEST
+/**
+ * Test that discovering priorities work, and the implicit pid
+ * overrides the explicit one.
+ */
START_TEST (test_discover_priority)
{
char *msg, *pid;
struct json_object *jo;
- openlog ("umberlog/test_discover_priority", 0, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_discover_priority", 0, LOG_LOCAL0);
+ ul_set_log_flags (LOG_UL_ALL);
msg = ul_format (LOG_DEBUG, "testing 1, 2, 3...",
"pid", "%d", getpid () + 42,
@@ -160,43 +303,24 @@ START_TEST (test_discover_priority)
json_object_put (jo);
- closelog ();
-}
-END_TEST
-
-START_TEST (test_no_timestamp)
-{
- char *msg;
- struct json_object *jo;
-
- openlog ("umberlog/test_no_timestamp", LOG_UL_NOTIME, LOG_LOCAL0);
-
- msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
- jo = parse_msg (msg);
- free (msg);
-
- verify_value (jo, "msg", "hello, I'm test_no_timestamp!");
- verify_value (jo, "facility", "local0");
- verify_value (jo, "priority", "debug");
- verify_value (jo, "program", "umberlog/test_no_timestamp");
- verify_value_exists (jo, "pid");
- verify_value_exists (jo, "uid");
- verify_value_exists (jo, "gid");
- verify_value_missing (jo, "timestamp");
- verify_value_exists (jo, "host");
-
- json_object_put (jo);
-
- closelog ();
+ ul_closelog ();
}
END_TEST
+/**
+ * Test for correct JSON escaping.
+ */
START_TEST (test_json_escape)
{
+ static const char control_chars[] =
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x20";
+
char *msg;
struct json_object *jo;
- openlog ("umberlog/test_json_escape", LOG_UL_NODISCOVER, LOG_LOCAL0);
+ ul_openlog ("umberlog/test_json_escape", 0, LOG_LOCAL0);
msg = ul_format (LOG_DEBUG, "%s", __FUNCTION__,
"quotes", "Hi, \"quoted value\" speaking!",
@@ -204,6 +328,7 @@ START_TEST (test_json_escape)
"control", "foo\nbar",
"utf8", "Árvíztűrő tükörfúrógép",
"junk", "\013foo",
+ "all_control", control_chars,
NULL);
jo = parse_msg (msg);
free (msg);
@@ -213,13 +338,18 @@ START_TEST (test_json_escape)
verify_value (jo, "control", "foo\nbar");
verify_value (jo, "utf8", "Árvíztűrő tükörfúrógép");
verify_value (jo, "junk", "\013foo");
+ verify_value (jo, "all_control", control_chars);
json_object_put (jo);
- closelog ();
+ ul_closelog ();
}
END_TEST
+/**
+ * Test that using a FACILITY | PRIORITY combo with ul_format has the
+ * desired result.
+ */
START_TEST (test_facprio)
{
char *msg;
@@ -237,23 +367,40 @@ START_TEST (test_facprio)
}
END_TEST
-START_TEST (test_closelog)
+#ifdef HAVE_PARSE_PRINTF_FORMAT
+/**
+ * Test parsing additional format strings, that are only supported if
+ * we're under glibc.
+ */
+START_TEST (test_positional_params)
{
char *msg;
struct json_object *jo;
- openlog ("umberlog/test_closelog", LOG_UL_NODISCOVER, LOG_LOCAL0);
- closelog ();
+ ul_openlog ("umberlog/test_positional_params", 0, LOG_LOCAL0);
- msg = ul_format (LOG_LOCAL1 | LOG_DEBUG, "%s", __FUNCTION__, NULL);
+#define COMPLEX_FORMAT \
+ "%3$*5$.*2$hhd , %1$Lf , %4$.3s , %4$s", 1.0L, 5, (char)100, "prefix", -8
+#define COMPLEX_RESULT "00100 , 1.000000 , pre , prefix"
+ msg = ul_format (LOG_DEBUG, COMPLEX_FORMAT,
+ "simple1", "value1",
+ "complex", COMPLEX_FORMAT,
+ "simple2", "value2",
+ NULL);
jo = parse_msg (msg);
free (msg);
- verify_value (jo, "facility", "local1");
+ verify_value (jo, "msg", COMPLEX_RESULT);
+ verify_value (jo, "simple1", "value1");
+ verify_value (jo, "complex", COMPLEX_RESULT);
+ verify_value (jo, "simple2", "value2");
json_object_put (jo);
+
+ ul_closelog ();
}
END_TEST
+#endif
int
main (void)
@@ -263,20 +410,25 @@ main (void)
TCase *ft, *bt;
int nfailed;
- s = suite_create ("Umberlog functional testsuite");
+ s = suite_create ("Umberlog (linkable) functional testsuite");
ft = tcase_create ("Basic tests");
- tcase_add_test (ft, test_simple);
- tcase_add_test (ft, test_no_discover);
+ tcase_add_test (ft, test_overrides);
+ tcase_add_test (ft, test_defaults);
+ tcase_add_test (ft, test_ul_openlog);
+ tcase_add_test (ft, test_ul_openlog_flag_ignore);
+ tcase_add_test (ft, test_closelog);
+ tcase_add_test (ft, test_no_implicit);
tcase_add_test (ft, test_additional_fields);
tcase_add_test (ft, test_discover_priority);
- tcase_add_test (ft, test_no_timestamp);
+#ifdef HAVE_PARSE_PRINTF_FORMAT
+ tcase_add_test (ft, test_positional_params);
+#endif
suite_add_tcase (s, ft);
bt = tcase_create ("Bug tests");
tcase_add_test (bt, test_json_escape);
tcase_add_test (bt, test_facprio);
- tcase_add_test (bt, test_closelog);
suite_add_tcase (s, bt);
sr = srunner_create (s);
diff --git a/t/test_umberlog_preload.c b/t/test_umberlog_preload.c
new file mode 100644
index 0000000..99451c6
--- /dev/null
+++ b/t/test_umberlog_preload.c
@@ -0,0 +1,145 @@
+#define _GNU_SOURCE 1
+
+#include "umberlog.h"
+#include "config.h"
+#include "test-common.h"
+#include <json.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <check.h>
+
+
+/** This must be the first test!
+ *
+ * This check verifies the openlog() defaults, and that additional
+ * fields are properly added.
+ */
+START_TEST (test_openlog_defaults)
+{
+ char *msg;
+ struct json_object *jo;
+
+ /* No openlog */
+
+ msg = ul_format (LOG_ALERT, "message", NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ verify_value (jo, "facility", "user");
+#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+ verify_value (jo, "program", "test_umberlog_preload");
+#else
+ verify_value_missing (jo, "program");
+#endif
+ verify_value_differs (jo, "pid", "0");
+ if (getuid () != 0)
+ verify_value_differs (jo, "uid", "0");
+ if (getgid () != 0)
+ verify_value_differs (jo, "gid", "0");
+ verify_value_differs (jo, "host", "");
+
+ json_object_put (jo);
+
+ closelog ();
+}
+END_TEST
+
+/**
+ * This verifies that adding any LOG_UL_* flags to openlog() will have
+ * no effect.
+ */
+START_TEST (test_openlog_flags)
+{
+ char *msg;
+ struct json_object *jo;
+ char host[_POSIX_HOST_NAME_MAX + 1];
+
+ openlog ("umberlog/test_openlog_flags", LOG_UL_NOIMPLICIT, LOG_LOCAL0);
+
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ gethostname (host, _POSIX_HOST_NAME_MAX);
+
+ verify_value (jo, "msg", "hello, I'm test_openlog_flags!");
+ verify_value (jo, "facility", "local0");
+ verify_value (jo, "priority", "debug");
+ verify_value (jo, "program", "umberlog/test_openlog_flags");
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_exists (jo, "timestamp");
+ verify_value (jo, "host", host);
+
+ json_object_put (jo);
+
+ closelog ();
+}
+END_TEST
+
+/**
+ * A simple test that options that should be respected, are respected.
+ */
+START_TEST (test_simple)
+{
+ char *msg;
+ struct json_object *jo;
+ char host[_POSIX_HOST_NAME_MAX + 1];
+
+ openlog ("umberlog/test_simple", 0, LOG_LOCAL0);
+
+ msg = ul_format (LOG_DEBUG, "hello, I'm %s!", __FUNCTION__, NULL);
+ jo = parse_msg (msg);
+ free (msg);
+
+ gethostname (host, _POSIX_HOST_NAME_MAX);
+
+ verify_value (jo, "msg", "hello, I'm test_simple!");
+ verify_value (jo, "facility", "local0");
+ verify_value (jo, "priority", "debug");
+ verify_value (jo, "program", "umberlog/test_simple");
+ verify_value_exists (jo, "pid");
+ verify_value_exists (jo, "uid");
+ verify_value_exists (jo, "gid");
+ verify_value_exists (jo, "timestamp");
+ verify_value (jo, "host", host);
+
+ json_object_put (jo);
+
+ closelog ();
+}
+END_TEST
+
+int
+main (void)
+{
+ Suite *s;
+ SRunner *sr;
+ TCase *ft, *bt;
+ int nfailed;
+
+ s = suite_create ("Umberlog (LD_PRELOAD) functional testsuite");
+
+#if DEFAULT_DISCOVER_FLAGS == LOG_UL_ALL
+ ft = tcase_create ("Basic tests");
+ tcase_add_test (ft, test_openlog_defaults);
+ tcase_add_test (ft, test_openlog_flags);
+ tcase_add_test (ft, test_simple);
+ suite_add_tcase (s, ft);
+#endif
+
+ sr = srunner_create (s);
+
+ srunner_run_all (sr, CK_ENV);
+ nfailed = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}