log: fix repeated invocation of vsnprintf()/vaprintf() in log_struct()

https://bugs.freedesktop.org/show_bug.cgi?id=55213
This commit is contained in:
Lennart Poettering 2012-09-24 23:22:19 +02:00
parent 1920e37ef9
commit 963ddb917d
4 changed files with 76 additions and 2 deletions

View File

@ -1307,6 +1307,12 @@ int main(int argc, char *argv[]) {
if (arg_user) {
/* Note that this resolves user names
* inside the container, and hence
* accesses the NSS modules from the
* container and not the host. This is
* a bit weird... */
if (get_user_creds((const char**)&arg_user, &uid, &gid, &home, NULL) < 0) {
log_error("get_user_creds() failed: %m");
goto child_fail;

View File

@ -27,6 +27,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
#include <printf.h>
#include "log.h"
#include "util.h"
@ -705,11 +706,23 @@ int log_struct_internal(
va_start(ap, format);
while (format && n + 1 < ELEMENTSOF(iovec)) {
char *buf;
va_list aq;
if (vasprintf(&buf, format, ap) < 0) {
/* We need to copy the va_list structure,
* since vasprintf() leaves it afterwards at
* an undefined location */
va_copy(aq, ap);
if (vasprintf(&buf, format, aq) < 0) {
va_end(aq);
r = -ENOMEM;
goto finish;
}
va_end(aq);
/* Now, jump enough ahead, so that we point to
* the next format string */
VA_FORMAT_ADVANCE(format, ap);
IOVEC_SET_STRING(iovec[n++], buf);
@ -742,8 +755,11 @@ int log_struct_internal(
va_start(ap, format);
while (format) {
va_list aq;
vsnprintf(buf, sizeof(buf), format, ap);
va_copy(aq, ap);
vsnprintf(buf, sizeof(buf), format, aq);
va_end(aq);
char_array_0(buf);
if (startswith(buf, "MESSAGE=")) {
@ -751,6 +767,8 @@ int log_struct_internal(
break;
}
VA_FORMAT_ADVANCE(format, ap);
format = va_arg(ap, char *);
}
va_end(ap);

View File

@ -193,4 +193,47 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
#define _cleanup_closedir_ __attribute__((cleanup(closedirp)))
#define _cleanup_umask_ __attribute__((cleanup(umaskp)))
#define VA_FORMAT_ADVANCE(format, ap) do { \
int _argtypes[64]; \
size_t _i, _k; \
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
for (_i = 0; _i < _k; _i++) { \
if (_argtypes[_i] & PA_FLAG_PTR) { \
(void) va_arg(ap, void*); \
continue; \
} \
\
switch (_argtypes[_i]) { \
case PA_INT: \
case PA_INT|PA_FLAG_SHORT: \
case PA_CHAR: \
(void) va_arg(ap, int); \
break; \
case PA_INT|PA_FLAG_LONG: \
(void) va_arg(ap, long int); \
break; \
case PA_INT|PA_FLAG_LONG_LONG: \
(void) va_arg(ap, long long int); \
break; \
case PA_WCHAR: \
(void) va_arg(ap, wchar_t); \
break; \
case PA_WSTRING: \
case PA_STRING: \
case PA_POINTER: \
(void) va_arg(ap, void*); \
break; \
case PA_FLOAT: \
case PA_DOUBLE: \
(void) va_arg(ap, double); \
break; \
case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
(void) va_arg(ap, long double); \
break; \
default: \
assert_not_reached("Unknown format string argument."); \
} \
} \
} while(false)
#include "log.h"

View File

@ -42,5 +42,12 @@ int main(int argc, char* argv[]) {
"SERVICE=foobar",
NULL);
log_struct(LOG_INFO,
"MESSAGE=Foobar PID=%lu", (unsigned long) getpid(),
"FORMAT_STR_TEST=1=%i A=%c 2=%hi 3=%li 4=%lli 1=%p foo=%s 2.5=%g 3.5=%g 4.5=%Lg",
(int) 1, 'A', (short) 2, (long int) 3, (long long int) 4, (void*) 1, "foo", (float) 2.5f, (double) 3.5, (long double) 4.5,
"SUFFIX=GOT IT",
NULL);
return 0;
}