From f4d139b399e1e5044fe6bb0ceecd4c72e63dac94 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 5 Dec 2018 18:29:15 +0100 Subject: core: Allow logging to an estream. * src/logging.c (set_file_fd): Add and use new arg 'stream'. (_gpgrt_log_set_sink): Implement setting an estream sink. * tests/t-logging.c: New test. * tests/Makefile.am (TESTS): Add test. Signed-off-by: Werner Koch --- src/logging.c | 21 ++++-- tests/Makefile.am | 2 +- tests/t-logging.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 tests/t-logging.c diff --git a/src/logging.c b/src/logging.c index 51e1362..42e7180 100644 --- a/src/logging.c +++ b/src/logging.c @@ -455,10 +455,10 @@ fun_closer (void *cookie_arg) /* Common function to either set the logging to a file or a file descriptor. */ static void -set_file_fd (const char *name, int fd) +set_file_fd (const char *name, int fd, estream_t stream) { estream_t fp; - int want_socket; + int want_socket = 0; #ifdef HAVE_W32CE_SYSTEM int use_writefile = 0; #endif @@ -472,6 +472,13 @@ set_file_fd (const char *name, int fd) logstream = NULL; } + if (stream) + { + /* We don't use a cookie to log directly to a stream. */ + fp = stream; + goto leave; + } + /* Figure out what kind of logging we want. */ if (name && !strcmp (name, "-")) { @@ -479,7 +486,6 @@ set_file_fd (const char *name, int fd) fd = _gpgrt_fileno (es_stderr); } - want_socket = 0; if (name && !strncmp (name, "tcp://", 6) && name[6]) want_socket = 1; #ifndef HAVE_W32_SYSTEM @@ -541,6 +547,7 @@ set_file_fd (const char *name, int fd) if (!fp) fp = es_stderr; + leave: _gpgrt_setvbuf (fp, NULL, _IOLBF, 0); logstream = fp; @@ -567,20 +574,20 @@ void _gpgrt_log_set_sink (const char *name, estream_t stream, int fd) { if (name && !stream && fd == -1) - set_file_fd (name, -1); + set_file_fd (name, -1, NULL); else if (!name && !stream && fd != -1) { if (!_gpgrt_fd_valid_p (fd)) _gpgrt_log_fatal ("gpgrt_log_set_sink: fd is invalid: %s\n", strerror (errno)); - set_file_fd (NULL, fd); + set_file_fd (NULL, fd, NULL); } else if (!name && stream && fd == -1) { - _gpgrt_log_fatal ("gpgrt_log_set_sink: stream arg not yet supported\n"); + set_file_fd (NULL, -1, stream); } else /* default */ - set_file_fd ("-", -1); + set_file_fd ("-", -1, NULL); } diff --git a/tests/Makefile.am b/tests/Makefile.am index b14fdc8..71ca3a4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,7 +28,7 @@ endif gpg_error_lib = ../src/libgpg-error.la TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64 \ - t-argparse + t-argparse t-logging AM_CPPFLAGS = -I$(top_builddir)/src $(extra_includes) diff --git a/tests/t-logging.c b/tests/t-logging.c new file mode 100644 index 0000000..e0f5e2a --- /dev/null +++ b/tests/t-logging.c @@ -0,0 +1,217 @@ +/* t-logging.c - Check the logging interface + * Copyright (C) 2018 g10 Code GmbH + * + * This file is part of Libgpg-error. + * + * Libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgpg-error is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#define PGM "t-logging" +#include "t-common.h" + +/* The memory based estream we use for logging. */ +static estream_t logmemfp; + + +static const char * +my_strusage (int level) +{ + const char *p; + + switch (level) + { + case 9: p = "LGPL-2.1-or-later"; break; + case 11: p = PGM; break; + default: p = NULL; + } + return p; +} + + +/* Read all data from the log stream into a new malloced buffer and return + * that buffer. The buffer is always 0 terminated. Either returns a + * string or dies. The stream will be truncated to zero. */ +static char * +log_to_string (void) +{ +#define NCHUNK 1024 + estream_t stream = gpgrt_log_get_stream (); + char *buffer; + size_t bufsize, buflen; + size_t nread; + + gpgrt_log_flush (); + gpgrt_rewind (stream); + + buffer = NULL; + buflen = bufsize = 0; + do + { + bufsize += NCHUNK; + buffer = realloc (buffer, bufsize+1); + if (!buffer) + die ("malloc failed at line %d\n", __LINE__); + + nread = gpgrt_fread (buffer + buflen, 1, NCHUNK, stream); + if (nread < NCHUNK && gpgrt_ferror (stream)) + die ("fread failed at line %d: %s\n", __LINE__, + gpg_strerror (gpg_err_code_from_syserror ())); + buflen += nread; + } + while (nread == NCHUNK); + buffer[nread] = 0; + + if (strlen (buffer) != buflen) + fail ("stream_to_string detected an embedded nul"); + + gpgrt_ftruncate (stream, 0); + return buffer; +#undef NCHUNK +} + + +static void +check_log_info (void) +{ + char *logbuf; + + log_info ("first log\n"); + logbuf = log_to_string (); + if (strcmp (logbuf, "t-logging: first log\n")) + fail ("log_info test failed at line %d\n", __LINE__); + free (logbuf); + + /* The second line should not have a LF. */ + log_info ("second log line"); + log_info ("third log line"); + logbuf = log_to_string (); + if (strcmp (logbuf, ("t-logging: second log line\n" + "t-logging: third log line"))) + fail ("log_info test failed at line %d\n", __LINE__); + free (logbuf); + + /* Now a multi line log. */ + log_info ("This is log line 1\nand 2\nand 3\n"); + logbuf = log_to_string (); + if (strcmp (logbuf, ("t-logging: This is log line 1\n" + "and 2\n" + "and 3\n"))) + fail ("log_info test failed at line %d\n", __LINE__); + free (logbuf); +} + + +static void +check_with_pid (void) +{ + char testbuf[100]; + char *logbuf; + + snprintf (testbuf, sizeof testbuf, "t-logging[%u]: ", + (unsigned int)getpid ()); + + log_info ("first log\n"); + logbuf = log_to_string (); + if (strncmp (logbuf, testbuf, strlen (testbuf)) + || strcmp (logbuf+strlen (testbuf), "first log\n")) + fail ("log_with_pid test failed at line %d\n", __LINE__); + free (logbuf); + + log_info ("This is log line 1\nand 2\nand 3\n"); + logbuf = log_to_string (); + if (strncmp (logbuf, testbuf, strlen (testbuf)) + || strcmp (logbuf+strlen (testbuf), ("This is log line 1\n" + "and 2\n" + "and 3\n"))) + fail ("log_with_pid test failed at line %d\n", __LINE__); + free (logbuf); +} + + +static void +check_log_error (void) +{ + char *logbuf; + + if (log_get_errorcount (0)) + fail ("log_get_errorcount() != 0 at line %d\n", __LINE__); + + log_error ("Hola, something went wrong\n"); + if (log_get_errorcount (0) != 1) + fail ("log_get_errorcount() != 1 at line %d\n", __LINE__); + logbuf = log_to_string (); + if (strcmp (logbuf, "t-logging: Hola, something went wrong\n")) + fail ("log_info test failed at line %d\n", __LINE__); + free (logbuf); + if (log_get_errorcount (0) != 1) + fail ("log_get_errorcount() != 1 at line %d\n", __LINE__); + if (log_get_errorcount (1) != 1) /* note: clear returns old value. */ + fail ("log_get_errorcount() != 1 at line %d\n", __LINE__); + if (log_get_errorcount (0)) + fail ("log_get_errorcount() != 0 after clear at line %d\n", __LINE__); +} + + +int +main (int argc, char **argv) +{ + gpgrt_opt_t opts[] = { + ARGPARSE_x ('v', "verbose", NONE, 0, "Print more diagnostics"), + ARGPARSE_s_n('d', "debug", "Flyswatter"), + ARGPARSE_end() + }; + gpgrt_argparse_t pargs = { &argc, &argv, 0 }; + + gpgrt_set_strusage (my_strusage); + gpgrt_log_set_prefix (gpgrt_strusage (11), GPGRT_LOG_WITH_PREFIX); + + while (gpgrt_argparse (NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case 'v': verbose++; break; + case 'd': debug++; break; + default : pargs.err = ARGPARSE_PRINT_ERROR; break; + } + } + gpgrt_argparse (NULL, &pargs, NULL); + + show ("testing logging using a memory log stream\n"); + logmemfp = gpgrt_fopenmem (0, "w+b"); + if (!logmemfp) + die ("fopenmem failed at line %d\n", __LINE__); + gpgrt_log_set_sink (NULL, logmemfp, -1); + + check_log_info (); + gpgrt_log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID); + check_with_pid (); + gpgrt_log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX); + check_log_error (); + + /* FIXME: Add more tests. */ + + show ("testing logging finished\n"); + return !!errorcount; +} -- cgit v1.2.1