diff options
| author | Gergely Nagy <algernon@balabit.hu> | 2012-03-24 19:05:46 +0100 |
|---|---|---|
| committer | Gergely Nagy <algernon@balabit.hu> | 2012-03-24 19:05:46 +0100 |
| commit | bf66fd9161e9dbcbffe8215d72612f3805cf25e1 (patch) | |
| tree | 058d160fd9f8a91ce659038c1913f01686477f6a | |
| parent | fcb18a0fdc2064a6ee39c58881a4ebc4a5661c68 (diff) | |
| parent | 9f6935065f4c844835774e8874fd38ad62363de0 (diff) | |
Merge branch 'master' into debian
| -rw-r--r-- | README.rst | 101 | ||||
| -rw-r--r-- | configure.ac | 12 | ||||
| -rw-r--r-- | lib/Makefile.am | 4 | ||||
| -rw-r--r-- | lib/umberlog.c | 209 | ||||
| -rw-r--r-- | lib/umberlog.h | 9 | ||||
| -rw-r--r-- | lib/umberlog.rst | 27 | ||||
| -rw-r--r-- | t/test_umberlog.c | 4 |
7 files changed, 262 insertions, 104 deletions
@@ -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"); |
