summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGergely Nagy <algernon@balabit.hu>2012-03-24 19:05:46 +0100
committerGergely Nagy <algernon@balabit.hu>2012-03-24 19:05:46 +0100
commitbf66fd9161e9dbcbffe8215d72612f3805cf25e1 (patch)
tree058d160fd9f8a91ce659038c1913f01686477f6a
parentfcb18a0fdc2064a6ee39c58881a4ebc4a5661c68 (diff)
parent9f6935065f4c844835774e8874fd38ad62363de0 (diff)
Merge branch 'master' into debian
-rw-r--r--README.rst101
-rw-r--r--configure.ac12
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/umberlog.c209
-rw-r--r--lib/umberlog.h9
-rw-r--r--lib/umberlog.rst27
-rw-r--r--t/test_umberlog.c4
7 files changed, 262 insertions, 104 deletions
diff --git a/README.rst b/README.rst
index ae8d351..5d30d59 100644
--- a/README.rst
+++ b/README.rst
@@ -1,31 +1,64 @@
-libumberlog
-===========
+What?
+=====
The libumberlog library serves two purposes: it's either a drop-in
replacement for the ``syslog()`` system call, in which case it turns
-the default syslog messages into CEE-enhanced messages, with a
-CEE-JSON payload, and some automatically discovered fields.
+the default syslog messages into `CEE\-enhanced messages`_, with a
+CEE-JSON payload, and some automatically discovered fields. Or, it can
+be used as a stand-alone library, that provides a ``syslog()``-like
+API, with the ability to add arbitrary key-value pairs to the
+resulting JSON payload.
-Or, it can be used as a stand-alone library, that provides a
-``syslog()``-like API, with the ability to add arbitrary key-value
-pairs to the resulting JSON payload.
+.. _CEE\-enhanced messages: #an-example
-Non-goals
----------
+Why?
+====
+
+The legacy ``syslog()`` interface, while simple, is starting to show
+its age. It was meant to be an interface to construct free-form
+messages, targeted at human readers. However, in this time and age,
+the amount of logs generated by a busy system is, especially by a
+central log server in a larger environment does not lend itself well
+to manual processing.
+
+Instead, we rely more and more on programs to make sense out of the
+logs, to structure the free-form text into something that's easier to
+search and corellate, to filter on, and the existing interface does
+not make this easy. It wasn't written with computer-based
+post-processing in mind.
+
+This library is an attempt to smoothly introduce structured logging to
+administrators and developers alike, by taking a legacy interface,
+``syslog()``, and improving on it a little. Not only by enhancing the
+existing function, for example with a high-resolution timestamp, but
+by providing an extended, but still similar API to developers, to
+allow them to add more structure to their logs.
+
+How?
+====
+
+An example
+----------
+
+One does wonder, how an example might look like, we're happy to
+oblige, and show one (word wrapped, for an easier read):
+
+SSH Login::
+
+ Mar 24 12:01:34 localhost sshd[12590]: @cee:{
+ "msg": "Accepted publickey for algernon from 127.0.0.1 port 55519 ssh2",
+ "pid": "12590", "facility": "auth", "priority": "info",
+ "program": "sshd", "uid": "0", "gid": "0",
+ "host": "hadhodrond", "timestamp": "2012-03-24T12:01:34.236987887+0100" }
-* It is not a goal to support anything else but ``syslog()`` payload.
-* It is not a goal to go to great lengths to discover things about the
- running process: only a few things that are easily available, no
- matter how reliable this information may be.
-* It is not a goal to support complex values, or anything other than
- plain C strings.
-
Requirements
------------
-Apart from the autotools, a C compiler, and json-c, there are no other
-hard dependencies when building, except for a sufficiently modern
-system.
+Apart from the autotools, a C compiler, and `json\-c`_, there are no
+other hard dependencies when building, except for a sufficiently
+modern system.
+
+.. _json\-c: http://oss.metaparadigm.com/json-c/
Installation
------------
@@ -43,17 +76,31 @@ 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
-umberlog(3) manual page for more information.
+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.
+
+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``.
-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``.
+.. _API documentation: http://algernon.github.com/libumberlog/umberlog.html
+Non-goals
+---------
+
+* It is not a goal to support anything else but ``syslog()`` payload.
+* It is not a goal to go to great lengths to discover things about the
+ running process: only a few things that are easily available, no
+ matter how reliable this information may be.
+* It is not a goal to support complex values, or anything other than
+ plain C strings.
+
License
-------
This library is released under a two-clause BSD license, see the
-LICENSE file for details.
+`LICENSE`_ file for details.
+
+.. _LICENSE: https://raw.github.com/algernon/libumberlog/master/LICENSE
diff --git a/configure.ac b/configure.ac
index f2c37b8..6879c3a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,18 @@ dnl ***************************************************************************
dnl Checks for libraries
AC_CHECK_FUNCS([gethostname strdup clock_gettime])
+dnl The dlopen() function is in the C library for *BSD and in
+dnl libdl on GLIBC-based systems
+AC_SEARCH_LIBS([dlopen], [dl dld], [], [
+ AC_MSG_ERROR([unable to find the dlopen() function])
+])
+
+dnl Similarly, clock_gettime is in the C library on *BSD, and in librt
+dnl on GLIBC-based systems.
+AC_SEARCH_LIBS([clock_gettime], [rt], [], [
+ AC_MSG_ERROR([unable to find the clock_gettime() function])
+])
+
dnl ***************************************************************************
dnl JSON-C headers/libraries
dnl ***************************************************************************
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 030ae4e..5c64fb6 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,7 +4,7 @@ LUL_AGE = 0
lib_LTLIBRARIES = libumberlog.la
libumberlog_la_LDFLAGS = -Wl,--version-script,${srcdir}/libumberlog.ld
-libumberlog_la_LIBADD = @JSON_LIBS@ -ldl -lrt
+libumberlog_la_LIBADD = @JSON_LIBS@
libumberlog_la_CFLAGS = @JSON_CFLAGS@
libumberlog_la_SOURCES = umberlog.c umberlog.h
@@ -14,3 +14,5 @@ libumberlog_include_HEADERS = umberlog.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libumberlog.pc
+
+EXTRA_DIST = umberlog.rst libumberlog.ld
diff --git a/lib/umberlog.c b/lib/umberlog.c
index 151b333..ae9ebaf 100644
--- a/lib/umberlog.c
+++ b/lib/umberlog.c
@@ -39,13 +39,16 @@
#include <syslog.h>
#include <limits.h>
#include <time.h>
+#include <errno.h>
#include "umberlog.h"
#if __USE_FORTIFY_LEVEL > 0
static void (*old_syslog_chk) ();
+static void (*old_vsyslog_chk) ();
#else
static void (*old_syslog) ();
+static void (*old_vsyslog) ();
#endif
static void (*old_openlog) ();
@@ -63,16 +66,20 @@ static __thread struct
uid_t uid;
gid_t gid;
const char *ident;
- char hostname[HOST_NAME_MAX + 1];
+ char hostname[_POSIX_HOST_NAME_MAX + 1];
} ul_sys_settings;
+static __thread int ul_recurse;
+
static void
ul_init (void)
{
#if __USE_FORTIFY_LEVEL > 0
old_syslog_chk = dlsym (RTLD_NEXT, "__syslog_chk");
+ old_vsyslog_chk = dlsym (RTLD_NEXT, "__vsyslog_chk");
#else
old_syslog = dlsym (RTLD_NEXT, "syslog");
+ old_vsyslog = dlsym (RTLD_NEXT, "vsyslog");
#endif
old_openlog = dlsym (RTLD_NEXT, "openlog");
old_setlogmask = dlsym (RTLD_NEXT, "setlogmask");
@@ -90,11 +97,11 @@ ul_openlog (const char *ident, int option, int facility)
ul_sys_settings.uid = getuid ();
ul_sys_settings.ident = ident;
- gethostname (ul_sys_settings.hostname, HOST_NAME_MAX);
+ gethostname (ul_sys_settings.hostname, _POSIX_HOST_NAME_MAX);
}
/** HELPERS **/
-static const char *
+static inline const char *
_find_facility (void)
{
int i = 0;
@@ -108,7 +115,7 @@ _find_facility (void)
return "<unknown>";
}
-static const char *
+static inline const char *
_find_prio (int prio)
{
int i = 0;
@@ -155,11 +162,11 @@ static inline const char *
_get_hostname (void)
{
if (ul_sys_settings.flags & LOG_UL_NOCACHE)
- gethostname (ul_sys_settings.hostname, HOST_NAME_MAX);
+ gethostname (ul_sys_settings.hostname, _POSIX_HOST_NAME_MAX);
return ul_sys_settings.hostname;
}
-static struct json_object *
+static inline struct json_object *
_ul_json_vappend (struct json_object *json, va_list ap)
{
char *key;
@@ -167,29 +174,40 @@ _ul_json_vappend (struct json_object *json, va_list ap)
while ((key = (char *)va_arg (ap, char *)) != NULL)
{
char *fmt = (char *)va_arg (ap, char *);
- char *value;
+ char *value = NULL;
+ struct json_object *jstr;
if (vasprintf (&value, fmt, ap) == -1)
- abort ();
- json_object_object_add (json, key, json_object_new_string (value));
+ return NULL;
+ if (!value)
+ return NULL;
+
+ jstr = json_object_new_string (value);
+ if (!jstr)
+ {
+ free (value);
+ return NULL;
+ }
+
+ json_object_object_add (json, key, jstr);
free (value);
}
return json;
}
-static struct json_object *
+static inline struct json_object *
_ul_json_append (struct json_object *json, ...)
{
va_list ap;
va_start (ap, json);
- _ul_json_vappend (json, ap);
+ json = _ul_json_vappend (json, ap);
va_end (ap);
return json;
}
-static inline void
+static inline struct json_object *
_ul_json_append_timestamp (struct json_object *jo)
{
struct timespec ts;
@@ -203,51 +221,63 @@ _ul_json_append_timestamp (struct json_object *jo)
strftime (stamp, sizeof (stamp), "%FT%T", tm);
strftime (zone, sizeof (zone), "%z", tm);
- _ul_json_append (jo, "timestamp", "%s.%lu%s",
- stamp, ts.tv_nsec, zone,
- NULL);
+ return _ul_json_append (jo, "timestamp", "%s.%lu%s",
+ stamp, ts.tv_nsec, zone,
+ NULL);
}
-static inline void
+static inline struct json_object *
_ul_discover (struct json_object *jo, int priority)
{
if (ul_sys_settings.flags & LOG_UL_NODISCOVER)
- return;
-
- _ul_json_append (jo,
- "pid", "%d", _find_pid (),
- "facility", "%s", _find_facility (),
- "priority", "%s", _find_prio (priority),
- "program", "%s", ul_sys_settings.ident,
- "uid", "%d", _get_uid (),
- "gid", "%d", _get_gid (),
- "host", "%s", _get_hostname (),
- NULL);
-
- if (ul_sys_settings.flags & LOG_UL_NOTIME)
- return;
-
- _ul_json_append_timestamp (jo);
+ return jo;
+
+ jo = _ul_json_append (jo,
+ "pid", "%d", _find_pid (),
+ "facility", "%s", _find_facility (),
+ "priority", "%s", _find_prio (priority),
+ "program", "%s", ul_sys_settings.ident,
+ "uid", "%d", _get_uid (),
+ "gid", "%d", _get_gid (),
+ "host", "%s", _get_hostname (),
+ NULL);
+
+ if (ul_sys_settings.flags & LOG_UL_NOTIME || !jo)
+ return jo;
+
+ return _ul_json_append_timestamp (jo);
}
-static struct json_object *
+static inline struct json_object *
_ul_vformat (struct json_object *jo, int format_version,
int priority, const char *msg_format,
va_list ap)
{
char *value;
+ struct json_object *jstr;
if (vasprintf (&value, msg_format, ap) == -1)
- abort ();
- json_object_object_add (jo, "msg", json_object_new_string (value));
+ return NULL;
+ if (!value)
+ return NULL;
+
+ jstr = json_object_new_string (value);
+ if (!jstr)
+ {
+ free (value);
+ return NULL;
+ }
+
+ json_object_object_add (jo, "msg", jstr);
free (value);
if (format_version > 0)
- _ul_json_vappend (jo, ap);
+ jo = _ul_json_vappend (jo, ap);
- _ul_discover (jo, priority);
+ if (!jo)
+ return NULL;
- return jo;
+ return _ul_discover (jo, priority);
}
static inline const char *
@@ -255,9 +285,12 @@ _ul_vformat_str (struct json_object *jo, int format_version,
int priority, const char *msg_format,
va_list ap)
{
- return json_object_to_json_string (_ul_vformat (jo, format_version,
- priority, msg_format,
- ap));
+ jo = _ul_vformat (jo, format_version,
+ priority, msg_format, ap);
+ if (!jo)
+ return NULL;
+
+ return json_object_to_json_string (jo);
}
/** Public API **/
@@ -279,52 +312,102 @@ ul_vformat (int priority, const char *msg_format, va_list ap)
{
struct json_object *jo = json_object_new_object ();
char *result;
+ const char *msg;
- result = strdup (_ul_vformat_str (jo, 1, priority, msg_format, ap));
- json_object_put (jo);
- return result;
-}
+ if (!jo)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
-void
-ul_syslog (int priority, const char *msg_format, ...)
-{
- va_list ap;
+ msg = _ul_vformat_str (jo, 1, priority, msg_format, ap);
+ if (!msg)
+ {
+ json_object_put (jo);
+ errno = ENOMEM;
+ return NULL;
+ }
- va_start (ap, msg_format);
- ul_vsyslog (priority, msg_format, ap);
- va_end (ap);
+ result = strdup (msg);
+ json_object_put (jo);
+ return result;
}
-static inline void
+static inline int
_ul_vsyslog (int format_version, int priority,
const char *msg_format, va_list ap)
{
struct json_object *jo;
+ const char *msg;
if (!(ul_sys_settings.mask & priority))
- return;
+ return 0;
+
+ jo = json_object_new_object ();
+ if (!jo)
+ return -1;
+
+ if (_ul_vformat (jo, format_version,
+ priority, msg_format, ap) == NULL)
+ {
+ json_object_put (jo);
+ return -1;
+ }
+
+ msg = json_object_to_json_string (jo);
+ if (!msg)
+ {
+ json_object_put (jo);
+ return -1;
+ }
- jo = _ul_vformat (json_object_new_object (), format_version,
- priority, msg_format, ap);
#if __USE_FORTIFY_LEVEL > 0
- old_syslog_chk (priority, __USE_FORTIFY_LEVEL - 1, "@cee:%s",
- json_object_to_json_string (jo));
+ old_syslog_chk (priority, __USE_FORTIFY_LEVEL - 1, "@cee:%s", msg);
#else
- old_syslog (priority, "@cee:%s", json_object_to_json_string (jo));
+ old_syslog (priority, "@cee:%s", msg);
#endif
+
json_object_put (jo);
+
+ return 0;
}
-void
+int
+ul_syslog (int priority, const char *msg_format, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start (ap, msg_format);
+ status = ul_vsyslog (priority, msg_format, ap);
+ va_end (ap);
+
+ return status;
+}
+
+int
ul_vsyslog (int priority, const char *msg_format, va_list ap)
{
- _ul_vsyslog (1, priority, msg_format, ap);
+ return _ul_vsyslog (1, priority, msg_format, ap);
}
void
ul_legacy_vsyslog (int priority, const char *msg_format, va_list ap)
{
- _ul_vsyslog (0, priority, msg_format, ap);
+ if (ul_recurse)
+ {
+#if __USE_FORTIFY_LEVEL > 0
+ old_vsyslog_chk (priority, __USE_FORTIFY_LEVEL - 1, msg_format, ap);
+#else
+ old_vsyslog (priority, msg_format, ap);
+#endif
+ }
+ else
+ {
+ ul_recurse = 1;
+ _ul_vsyslog (0, priority, msg_format, ap);
+ }
+ ul_recurse = 0;
}
void
@@ -366,9 +449,11 @@ __vsyslog_chk (int __pri, int __flag, __const char *__fmt, va_list ap)
void openlog (const char *ident, int option, int facility)
__attribute__((alias ("ul_openlog")));
+#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/lib/umberlog.h b/lib/umberlog.h
index f91fe67..4245dae 100644
--- a/lib/umberlog.h
+++ b/lib/umberlog.h
@@ -36,14 +36,17 @@
#define LOG_UL_NOCACHE_UID 0x0100
#define LOG_UL_NOTIME 0x0200
-char *ul_format (int priority, const char *msg_format, ...);
+char *ul_format (int priority, const char *msg_format, ...)
+ __attribute__((sentinel));
char *ul_vformat (int priority, const char *msg_format, va_list ap);
void ul_openlog (const char *ident, int option, int facility);
int ul_setlogmask (int mask);
-void ul_syslog (int priority, const char *msg_format, ...);
-void ul_vsyslog (int priority, const char *msg_format, va_list ap);
+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));
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 036341b..26204e2 100644
--- a/lib/umberlog.rst
+++ b/lib/umberlog.rst
@@ -20,8 +20,8 @@ SYNOPSIS
void ul_openlog (const char *ident, int option, int facility);
- void ul_syslog (int priority, const char *format, ....);
- void ul_vsyslog (int priority, const char *format, va_list ap);
+ int ul_syslog (int priority, const char *format, ....);
+ int ul_vsyslog (int priority, const char *format, va_list ap);
void ul_legacy_syslog (int priority, const char *format, ...);
void ul_legacy_vsyslog (int priority, const char *format, va_list ap);
@@ -44,8 +44,8 @@ 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.
-_syslog()** and **ul_vsyslog()** are two new functions provided by the
-library, that have similar interface to the legacy **syslog()**
+**ul_syslog()** and **ul_vsyslog()** are two new functions provided by
+the library, that have similar interface to the legacy **syslog()**
functions, but they can be used to add arbitrary key-value pairs to
the emitted message. After the *msg_format* format string, and any
other parameters it refers to, there must be a NULL-terminated list of
@@ -57,6 +57,15 @@ will be added to the generated message.
variants above, except the formatted payload is not sent to syslog,
but returned as a newly allocated string.
+RETURN VALUE
+============
+
+When successful, **ul_syslog()** and **ul_vsyslog()** return zero,
+while **ul_format()** and **ul_vformat()** return a character string.
+
+On failure the former two will return non-zero, the latter two
+**NULL**, and set *errno* appropriately.
+
CEE PAYLOAD
===========
@@ -122,11 +131,11 @@ EXAMPLES
::
- ul_syslog(LOG_NOTICE, "Logged in user: %s", username,
- "service", "%s", service,
- "auth-method", "%s", auth_method,
- "sessionid", "%d", session_id,
- NULL);
+ status = ul_syslog(LOG_NOTICE, "Logged in user: %s", username,
+ "service", "%s", service,
+ "auth-method", "%s", auth_method,
+ "sessionid", "%d", session_id,
+ NULL);
SEE ALSO
========
diff --git a/t/test_umberlog.c b/t/test_umberlog.c
index d1c2911..f20d28b 100644
--- a/t/test_umberlog.c
+++ b/t/test_umberlog.c
@@ -61,7 +61,7 @@ test_simple (void)
{
char *msg;
struct json_object *jo;
- char host[HOST_NAME_MAX + 1];
+ char host[_POSIX_HOST_NAME_MAX + 1];
openlog ("umberlog/test_simple", 0, LOG_LOCAL0);
@@ -69,7 +69,7 @@ test_simple (void)
jo = parse_msg (msg);
free (msg);
- gethostname (host, HOST_NAME_MAX);
+ gethostname (host, _POSIX_HOST_NAME_MAX);
verify_value (jo, "msg", "hello, I'm test_simple!");
verify_value (jo, "facility", "local0");