diff options
| author | Gergely Nagy <algernon@balabit.hu> | 2012-08-13 23:05:50 +0200 |
|---|---|---|
| committer | Gergely Nagy <algernon@balabit.hu> | 2012-08-13 23:05:50 +0200 |
| commit | af1cbe2c1d4c3aa749b7f723e741a4a4df1a321a (patch) | |
| tree | bfef4a37932b6f90441e4b55a3a7ea6d01e545c2 /lib | |
| parent | 3c2213da88c3dfc957a8505f5addc5194901c92f (diff) | |
| parent | 2b2c352e1ec33eaea956a2b039840686be14b32c (diff) | |
Merge tag 'libumberlog-0.3.0' into debian
libumberlog 0.3.0 release
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile.am | 16 | ||||
| -rw-r--r-- | lib/buffer.c | 273 | ||||
| -rw-r--r-- | lib/buffer.h | 8 | ||||
| -rw-r--r-- | lib/libumberlog.ld | 9 | ||||
| -rw-r--r-- | lib/umberlog.c | 602 | ||||
| -rw-r--r-- | lib/umberlog.h | 14 | ||||
| -rw-r--r-- | lib/umberlog.rst | 82 | ||||
| -rw-r--r-- | lib/umberlog_preload.c | 64 |
8 files changed, 676 insertions, 392 deletions
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"))); |
