summaryrefslogtreecommitdiffstats
path: root/comm/third_party/libgpg-error/src/logging.c
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/libgpg-error/src/logging.c')
-rw-r--r--comm/third_party/libgpg-error/src/logging.c1341
1 files changed, 1341 insertions, 0 deletions
diff --git a/comm/third_party/libgpg-error/src/logging.c b/comm/third_party/libgpg-error/src/logging.c
new file mode 100644
index 0000000000..e4b7e401d0
--- /dev/null
+++ b/comm/third_party/libgpg-error/src/logging.c
@@ -0,0 +1,1341 @@
+/* logging.c - Useful logging functions
+ * Copyright (C) 1998-2001, 2003-2006, 2009-2010,
+ * 2017 Free Software Foundation, Inc.
+ * Copyright (C) 1998-1999, 2001-2006, 2008-2017 Werner Koch
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * This file was originally a part of GnuPG.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+# endif
+# include <windows.h>
+#else /*!HAVE_W32_SYSTEM*/
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif /*!HAVE_W32_SYSTEM*/
+#include <unistd.h>
+#include <fcntl.h>
+/* #include <execinfo.h> */
+
+#define _GPGRT_NEED_AFLOCAL 1
+#include "gpgrt-int.h"
+
+
+#ifdef HAVE_W32_SYSTEM
+# ifndef S_IRWXG
+# define S_IRGRP S_IRUSR
+# define S_IWGRP S_IWUSR
+# endif
+# ifndef S_IRWXO
+# define S_IROTH S_IRUSR
+# define S_IWOTH S_IWUSR
+# endif
+#endif
+
+
+#ifdef HAVE_W32CE_SYSTEM
+# define isatty(a) (0)
+#endif
+
+#undef WITH_IPV6
+#if defined (AF_INET6) && defined(PF_INET) \
+ && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
+# define WITH_IPV6 1
+#endif
+
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT EINVAL
+#endif
+#ifndef INADDR_NONE /* Slowaris is missing that. */
+#define INADDR_NONE ((unsigned long)(-1))
+#endif /*INADDR_NONE*/
+
+#ifdef HAVE_W32_SYSTEM
+#define sock_close(a) closesocket(a)
+#else
+#define sock_close(a) close(a)
+#endif
+
+
+static estream_t logstream;
+static int log_socket = -1;
+static char prefix_buffer[80];
+static int with_time;
+static int with_prefix;
+static int with_pid;
+#ifdef HAVE_W32_SYSTEM
+static int no_registry;
+#endif
+static int (*get_pid_suffix_cb)(unsigned long *r_value);
+static const char * (*socket_dir_cb)(void);
+static int running_detached;
+static int force_prefixes;
+
+static int missing_lf;
+static int errorcount;
+
+
+/* An object to convey data to the fmt_string_filter. */
+struct fmt_string_filter_s
+{
+ char *last_result;
+};
+
+
+
+/* Get the error count as maintained by the log fucntions. With CLEAR
+ * set reset the counter. */
+int
+_gpgrt_get_errorcount (int clear)
+{
+ int n = errorcount;
+ if (clear)
+ errorcount = 0;
+ return n;
+}
+
+
+/* Increment the error count as maintained by the log functions. */
+void
+_gpgrt_inc_errorcount (void)
+{
+ /* Protect against counter overflow. */
+ if (errorcount < 30000)
+ errorcount++;
+}
+
+
+/* The following 3 functions are used by _gpgrt_fopencookie to write logs
+ to a socket. */
+struct fun_cookie_s
+{
+ int fd;
+ int quiet;
+ int want_socket;
+ int is_socket;
+#ifdef HAVE_W32CE_SYSTEM
+ int use_writefile;
+#endif
+ char name[1];
+};
+
+
+/* Write NBYTES of BUFFER to file descriptor FD. */
+static int
+writen (int fd, const void *buffer, size_t nbytes, int is_socket)
+{
+ const char *buf = buffer;
+ size_t nleft = nbytes;
+ int nwritten;
+#ifndef HAVE_W32_SYSTEM
+ (void)is_socket; /* Not required. */
+#endif
+
+ while (nleft > 0)
+ {
+#ifdef HAVE_W32_SYSTEM
+ if (is_socket)
+ nwritten = send (fd, buf, nleft, 0);
+ else
+#endif
+ nwritten = write (fd, buf, nleft);
+
+ if (nwritten < 0 && errno == EINTR)
+ continue;
+ if (nwritten < 0)
+ return -1;
+ nleft -= nwritten;
+ buf = buf + nwritten;
+ }
+
+ return 0;
+}
+
+
+/* Returns true if STR represents a valid port number in decimal
+ notation and no garbage is following. */
+static int
+parse_portno (const char *str, unsigned short *r_port)
+{
+ unsigned int value;
+
+ for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
+ {
+ value = value * 10 + (*str - '0');
+ if (value > 65535)
+ return 0;
+ }
+ if (*str || !value)
+ return 0;
+
+ *r_port = value;
+ return 1;
+}
+
+
+static gpgrt_ssize_t
+fun_writer (void *cookie_arg, const void *buffer, size_t size)
+{
+ struct fun_cookie_s *cookie = cookie_arg;
+
+ /* FIXME: Use only estream with a callback for socket writing. This
+ avoids the ugly mix of fd and estream code. */
+
+ /* Note that we always try to reconnect to the socket but print
+ error messages only the first time an error occurred. If
+ RUNNING_DETACHED is set we don't fall back to stderr and even do
+ not print any error messages. This is needed because detached
+ processes often close stderr and by writing to file descriptor 2
+ we might send the log message to a file not intended for logging
+ (e.g. a pipe or network connection). */
+ if (cookie->want_socket && cookie->fd == -1)
+ {
+#ifdef WITH_IPV6
+ struct sockaddr_in6 srvr_addr_in6;
+#endif
+ struct sockaddr_in srvr_addr_in;
+#ifndef HAVE_W32_SYSTEM
+ struct sockaddr_un srvr_addr_un;
+#endif
+ const char *name_for_err = "";
+ size_t addrlen;
+ struct sockaddr *srvr_addr = NULL;
+ unsigned short port = 0;
+ int af = AF_LOCAL;
+ int pf = PF_LOCAL;
+ const char *name = cookie->name;
+
+ /* Not yet open or meanwhile closed due to an error. */
+ cookie->is_socket = 0;
+
+ /* Check whether this is a TCP socket or a local socket. */
+ if (!strncmp (name, "tcp://", 6) && name[6])
+ {
+ name += 6;
+ af = AF_INET;
+ pf = PF_INET;
+ }
+#ifndef HAVE_W32_SYSTEM
+ else if (!strncmp (name, "socket://", 9))
+ name += 9;
+#endif
+
+ if (af == AF_LOCAL)
+ {
+ addrlen = 0;
+#ifndef HAVE_W32_SYSTEM
+ memset (&srvr_addr, 0, sizeof srvr_addr);
+ srvr_addr_un.sun_family = af;
+ if (!*name)
+ {
+ if ((name = socket_dir_cb ()) && *name
+ && strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1)
+ {
+ strncpy (srvr_addr_un.sun_path,
+ name, sizeof (srvr_addr_un.sun_path)-1);
+ strcat (srvr_addr_un.sun_path, "/S.log");
+ srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
+ srvr_addr = (struct sockaddr *)&srvr_addr_un;
+ addrlen = SUN_LEN (&srvr_addr_un);
+ name_for_err = srvr_addr_un.sun_path;
+ }
+ }
+ else
+ {
+ if (strlen (name) < sizeof (srvr_addr_un.sun_path)-1)
+ {
+ strncpy (srvr_addr_un.sun_path,
+ name, sizeof (srvr_addr_un.sun_path)-1);
+ srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
+ srvr_addr = (struct sockaddr *)&srvr_addr_un;
+ addrlen = SUN_LEN (&srvr_addr_un);
+ }
+ }
+#endif /*!HAVE_W32SYSTEM*/
+ }
+ else
+ {
+ char *addrstr, *p;
+#ifdef HAVE_INET_PTON
+ void *addrbuf = NULL;
+#endif /*HAVE_INET_PTON*/
+
+ addrstr = _gpgrt_malloc (strlen (name) + 1);
+ if (!addrstr)
+ addrlen = 0; /* This indicates an error. */
+ else if (*name == '[')
+ {
+ /* Check for IPv6 literal address. */
+ strcpy (addrstr, name+1);
+ p = strchr (addrstr, ']');
+ if (!p || p[1] != ':' || !parse_portno (p+2, &port))
+ {
+ _gpg_err_set_errno (EINVAL);
+ addrlen = 0;
+ }
+ else
+ {
+ *p = 0;
+#ifdef WITH_IPV6
+ af = AF_INET6;
+ pf = PF_INET6;
+ memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
+ srvr_addr_in6.sin6_family = af;
+ srvr_addr_in6.sin6_port = htons (port);
+#ifdef HAVE_INET_PTON
+ addrbuf = &srvr_addr_in6.sin6_addr;
+#endif /*HAVE_INET_PTON*/
+ srvr_addr = (struct sockaddr *)&srvr_addr_in6;
+ addrlen = sizeof srvr_addr_in6;
+#else
+ _gpg_err_set_errno (EAFNOSUPPORT);
+ addrlen = 0;
+#endif
+ }
+ }
+ else
+ {
+ /* Check for IPv4 literal address. */
+ strcpy (addrstr, name);
+ p = strchr (addrstr, ':');
+ if (!p || !parse_portno (p+1, &port))
+ {
+ _gpg_err_set_errno (EINVAL);
+ addrlen = 0;
+ }
+ else
+ {
+ *p = 0;
+ memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
+ srvr_addr_in.sin_family = af;
+ srvr_addr_in.sin_port = htons (port);
+#ifdef HAVE_INET_PTON
+ addrbuf = &srvr_addr_in.sin_addr;
+#endif /*HAVE_INET_PTON*/
+ srvr_addr = (struct sockaddr *)&srvr_addr_in;
+ addrlen = sizeof srvr_addr_in;
+ }
+ }
+
+ if (addrlen)
+ {
+#ifdef HAVE_INET_PTON
+ if (inet_pton (af, addrstr, addrbuf) != 1)
+ addrlen = 0;
+#else /*!HAVE_INET_PTON*/
+ /* We need to use the old function. If we are here v6
+ support isn't enabled anyway and thus we can do fine
+ without. Note that Windows has a compatible inet_pton
+ function named inetPton, but only since Vista. */
+ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
+ if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
+ addrlen = 0;
+#endif /*!HAVE_INET_PTON*/
+ }
+
+ _gpgrt_free (addrstr);
+ }
+
+ cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1;
+ if (cookie->fd == -1)
+ {
+ if (!cookie->quiet && !running_detached
+ && isatty (_gpgrt_fileno (es_stderr)))
+ _gpgrt_fprintf (es_stderr,
+ "failed to create socket for logging: %s\n",
+ strerror (errno));
+ }
+ else
+ {
+ if (connect (cookie->fd, srvr_addr, addrlen) == -1)
+ {
+ if (!cookie->quiet && !running_detached
+ && isatty (_gpgrt_fileno (es_stderr)))
+ _gpgrt_fprintf (es_stderr, "can't connect to '%s%s': %s\n",
+ cookie->name, name_for_err, strerror(errno));
+ sock_close (cookie->fd);
+ cookie->fd = -1;
+ }
+ }
+
+ if (cookie->fd == -1)
+ {
+ if (!running_detached)
+ {
+ /* Due to all the problems with apps not running
+ detached but being called with stderr closed or used
+ for a different purposes, it does not make sense to
+ switch to stderr. We therefore disable it. */
+ if (!cookie->quiet)
+ {
+ /* fputs ("switching logging to stderr\n", stderr);*/
+ cookie->quiet = 1;
+ }
+ cookie->fd = -1; /*fileno (stderr);*/
+ }
+ }
+ else /* Connection has been established. */
+ {
+ cookie->quiet = 0;
+ cookie->is_socket = 1;
+ }
+ }
+
+ log_socket = cookie->fd;
+ if (cookie->fd != -1)
+ {
+#ifdef HAVE_W32CE_SYSTEM
+ if (cookie->use_writefile)
+ {
+ DWORD nwritten;
+
+ WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL);
+ return (gpgrt_ssize_t)size; /* Okay. */
+ }
+#endif
+ if (!writen (cookie->fd, buffer, size, cookie->is_socket))
+ return (gpgrt_ssize_t)size; /* Okay. */
+ }
+
+ if (!running_detached && cookie->fd != -1
+ && isatty (_gpgrt_fileno (es_stderr)))
+ {
+ if (*cookie->name)
+ _gpgrt_fprintf (es_stderr, "error writing to '%s': %s\n",
+ cookie->name, strerror(errno));
+ else
+ _gpgrt_fprintf (es_stderr, "error writing to file descriptor %d: %s\n",
+ cookie->fd, strerror(errno));
+ }
+ if (cookie->is_socket && cookie->fd != -1)
+ {
+ sock_close (cookie->fd);
+ cookie->fd = -1;
+ log_socket = -1;
+ }
+
+ return (gpgrt_ssize_t)size;
+}
+
+
+static int
+fun_closer (void *cookie_arg)
+{
+ struct fun_cookie_s *cookie = cookie_arg;
+
+ if (cookie->fd != -1 && cookie->fd != 2)
+ sock_close (cookie->fd);
+ _gpgrt_free (cookie);
+ log_socket = -1;
+ return 0;
+}
+
+
+/* Common function to either set the logging to a file or a file
+ descriptor. */
+static void
+set_file_fd (const char *name, int fd, estream_t stream)
+{
+ estream_t fp;
+ int want_socket = 0;
+#ifdef HAVE_W32CE_SYSTEM
+ int use_writefile = 0;
+#endif
+ struct fun_cookie_s *cookie;
+
+ /* Close an open log stream. */
+ if (logstream)
+ {
+ if (logstream != es_stderr)
+ _gpgrt_fclose (logstream);
+ 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, "-"))
+ {
+ name = NULL;
+ fd = _gpgrt_fileno (es_stderr);
+ }
+
+ if (name && !strncmp (name, "tcp://", 6) && name[6])
+ want_socket = 1;
+#ifndef HAVE_W32_SYSTEM
+ else if (name && !strncmp (name, "socket://", 9))
+ want_socket = 2;
+#endif /*HAVE_W32_SYSTEM*/
+#ifdef HAVE_W32CE_SYSTEM
+ else if (name && !strcmp (name, "GPG2:"))
+ {
+ HANDLE hd;
+
+ ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0);
+ /* Ignore a filename and write the debug output to the GPG2:
+ device. */
+ hd = CreateFile (L"GPG2:", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd;
+ name = NULL;
+ force_prefixes = 1;
+ use_writefile = 1;
+ }
+#endif /*HAVE_W32CE_SYSTEM*/
+
+ /* Setup a new stream. */
+
+ cookie = _gpgrt_malloc (sizeof *cookie + (name? strlen (name):0));
+ if (!cookie)
+ return; /* oops */
+ strcpy (cookie->name, name? name:"");
+ cookie->quiet = 0;
+ cookie->is_socket = 0;
+ cookie->want_socket = want_socket;
+#ifdef HAVE_W32CE_SYSTEM
+ cookie->use_writefile = use_writefile;
+#endif
+ if (!name)
+ cookie->fd = fd;
+ else if (want_socket)
+ cookie->fd = -1;
+ else
+ {
+ do
+ cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT,
+ (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH));
+ while (cookie->fd == -1 && errno == EINTR);
+ }
+ log_socket = cookie->fd;
+
+ {
+ es_cookie_io_functions_t io = { NULL };
+ io.func_write = fun_writer;
+ io.func_close = fun_closer;
+
+ fp = _gpgrt_fopencookie (cookie, "w", io);
+ }
+
+ /* On error default to a stderr based estream. */
+ if (!fp)
+ fp = es_stderr;
+
+ leave:
+ _gpgrt_setvbuf (fp, NULL, _IOLBF, 0);
+
+ logstream = fp;
+
+ /* We always need to print the prefix and the pid for socket mode,
+ so that the server reading the socket can do something
+ meaningful. */
+ force_prefixes = want_socket;
+
+ missing_lf = 0;
+}
+
+
+/* Set the file to write log to. The special names NULL and "-" may
+ * be used to select stderr and names formatted like
+ * "socket:///home/foo/mylogs" may be used to write the logging to the
+ * socket "/home/foo/mylogs". If the connection to the socket fails
+ * or a write error is detected, the function writes to stderr and
+ * tries the next time again to connect the socket. Calling this
+ * function with (NULL, NULL, -1) sets the default sink.
+ * Warning: This function is not thread-safe.
+ */
+void
+_gpgrt_log_set_sink (const char *name, estream_t stream, int fd)
+{
+ if (name && !stream && fd == -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, NULL);
+ }
+ else if (!name && stream && fd == -1)
+ {
+ set_file_fd (NULL, -1, stream);
+ }
+ else /* default */
+ set_file_fd ("-", -1, NULL);
+}
+
+
+/* Set a function to retrieve the directory name of a socket if
+ * only "socket://" has been given to log_set_file.
+ * Warning: This function is not thread-safe. */
+void
+_gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void))
+{
+ socket_dir_cb = fnc;
+}
+
+
+/* Warning: This function is not thread-safe. */
+void
+_gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
+{
+ get_pid_suffix_cb = cb;
+}
+
+
+/* Warning: Changing TEXT is not thread-safe. Changing only flags
+ * might be thread-safe. */
+void
+_gpgrt_log_set_prefix (const char *text, unsigned int flags)
+{
+ if (text)
+ {
+ strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
+ prefix_buffer[sizeof (prefix_buffer)-1] = 0;
+ }
+
+ with_prefix = (flags & GPGRT_LOG_WITH_PREFIX);
+ with_time = (flags & GPGRT_LOG_WITH_TIME);
+ with_pid = (flags & GPGRT_LOG_WITH_PID);
+ running_detached = (flags & GPGRT_LOG_RUN_DETACHED);
+#ifdef HAVE_W32_SYSTEM
+ no_registry = (flags & GPGRT_LOG_NO_REGISTRY);
+#endif
+}
+
+
+const char *
+_gpgrt_log_get_prefix (unsigned int *flags)
+{
+ if (flags)
+ {
+ *flags = 0;
+ if (with_prefix)
+ *flags |= GPGRT_LOG_WITH_PREFIX;
+ if (with_time)
+ *flags |= GPGRT_LOG_WITH_TIME;
+ if (with_pid)
+ *flags |= GPGRT_LOG_WITH_PID;
+ if (running_detached)
+ *flags |= GPGRT_LOG_RUN_DETACHED;
+#ifdef HAVE_W32_SYSTEM
+ if (no_registry)
+ *flags |= GPGRT_LOG_NO_REGISTRY;
+#endif
+ }
+ return prefix_buffer;
+}
+
+/* This function returns true if the file descriptor FD is in use for
+ * logging. This is preferable over a test using log_get_fd in that
+ * it allows the logging code to use more then one file descriptor. */
+int
+_gpgrt_log_test_fd (int fd)
+{
+ if (logstream)
+ {
+ int tmp = _gpgrt_fileno (logstream);
+ if ( tmp != -1 && tmp == fd)
+ return 1;
+ }
+ if (log_socket != -1 && log_socket == fd)
+ return 1;
+ return 0;
+}
+
+int
+_gpgrt_log_get_fd ()
+{
+ return logstream? _gpgrt_fileno (logstream) : -1;
+}
+
+estream_t
+_gpgrt_log_get_stream ()
+{
+ if (!logstream)
+ {
+ /* Make sure a log stream has been set. */
+ _gpgrt_log_set_sink (NULL, NULL, -1);
+ if (!logstream)
+ {
+ fputs ("gpgrt fatal: failed to init log stream\n", stderr);
+ _gpgrt_abort ();
+ }
+ }
+ return logstream;
+}
+
+
+/* A filter used with the fprintf_sf function to sanitize the args for
+ * "%s" format specifiers. */
+static char *
+fmt_string_filter (const char *string, int no, void *opaque)
+{
+ struct fmt_string_filter_s *state = opaque;
+ const unsigned char *p;
+ size_t buflen;
+ char *d;
+ int any;
+
+ if (no == -1)
+ {
+ /* The printf engine asked us to release resources. */
+ if (state->last_result)
+ {
+ _gpgrt_free (state->last_result);
+ state->last_result = NULL;
+ }
+ return NULL;
+ }
+
+ if (!string)
+ return NULL; /* Nothing to filter - printf handles NULL nicely. */
+
+ /* Check whether escaping is needed and count needed length. */
+ any = 0;
+ buflen = 1;
+ for (p = (const unsigned char *)string; *p; p++)
+ {
+ switch (*p)
+ {
+ case '\n':
+ case '\r':
+ case '\f':
+ case '\v':
+ case '\b':
+ case '\t':
+ case '\a':
+ case '\\':
+ buflen += 2;
+ any = 1;
+ break;
+ default:
+ if (*p < 0x20 || *p == 0x7f)
+ {
+ buflen += 5;
+ any = 1;
+ }
+ else
+ buflen++;
+ }
+ }
+ if (!any)
+ return (char*)string; /* Nothing to escape. */
+
+ /* Create a buffer and escape the input. */
+ _gpgrt_free (state->last_result);
+ state->last_result = _gpgrt_malloc (buflen);
+ if (!state->last_result)
+ return "[out_of_core_in_format_string_filter]";
+
+ d = state->last_result;
+ for (p = (const unsigned char *)string; *p; p++)
+ {
+ switch (*p)
+ {
+ case '\n': *d++ = '\\'; *d++ = 'n'; break;
+ case '\r': *d++ = '\\'; *d++ = 'r'; break;
+ case '\f': *d++ = '\\'; *d++ = 'f'; break;
+ case '\v': *d++ = '\\'; *d++ = 'v'; break;
+ case '\b': *d++ = '\\'; *d++ = 'b'; break;
+ case '\t': *d++ = '\\'; *d++ = 't'; break;
+ case '\a': *d++ = '\\'; *d++ = 'a'; break;
+ case '\\': *d++ = '\\'; *d++ = '\\'; break;
+
+ default:
+ if (*p < 0x20 || *p == 0x7f)
+ {
+ snprintf (d, 5, "\\x%02x", *p);
+ d += 4;
+ }
+ else
+ *d++ = *p;
+ }
+ }
+ *d = 0;
+ return state->last_result;
+}
+
+
+/* Note: LOGSTREAM is expected to be locked. */
+static int
+print_prefix (int level, int leading_backspace)
+{
+ int rc;
+ int length = 0;
+
+ if (level != GPGRT_LOGLVL_CONT)
+ { /* Note this does not work for multiple line logging as we would
+ * need to print to a buffer first */
+ if (with_time && !force_prefixes)
+ {
+ struct tm *tp;
+ time_t atime = time (NULL);
+
+ tp = localtime (&atime);
+ rc = _gpgrt_fprintf_unlocked (logstream,
+ "%04d-%02d-%02d %02d:%02d:%02d ",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec );
+ if (rc > 0)
+ length += rc;
+ }
+ if (with_prefix || force_prefixes)
+ {
+ _gpgrt_fputs_unlocked (prefix_buffer, logstream);
+ length += strlen (prefix_buffer);
+ }
+ if (with_pid || force_prefixes)
+ {
+ unsigned long pidsuf;
+ int pidfmt;
+
+ if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
+ rc = _gpgrt_fprintf_unlocked (logstream,
+ pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
+ (unsigned int)getpid (), pidsuf);
+ else
+ rc = _gpgrt_fprintf_unlocked (logstream, "[%u]",
+ (unsigned int)getpid ());
+ if (rc > 0)
+ length += rc;
+ }
+ if ((!with_time && (with_prefix || with_pid)) || force_prefixes)
+ {
+ _gpgrt_putc_unlocked (':', logstream);
+ length++;
+ }
+ /* A leading backspace suppresses the extra space so that we can
+ correctly output, programname, filename and linenumber. */
+ if (!leading_backspace
+ && (with_time || with_prefix || with_pid || force_prefixes))
+ {
+ _gpgrt_putc_unlocked (' ', logstream);
+ length++;
+ }
+ }
+
+ switch (level)
+ {
+ case GPGRT_LOGLVL_BEGIN: break;
+ case GPGRT_LOGLVL_CONT: break;
+ case GPGRT_LOGLVL_INFO: break;
+ case GPGRT_LOGLVL_WARN: break;
+ case GPGRT_LOGLVL_ERROR: break;
+ case GPGRT_LOGLVL_FATAL:
+ _gpgrt_fputs_unlocked ("Fatal: ", logstream);
+ length += 7;
+ break;
+ case GPGRT_LOGLVL_BUG:
+ _gpgrt_fputs_unlocked ("Ohhhh jeeee: ", logstream);
+ length += 13;
+ break;
+ case GPGRT_LOGLVL_DEBUG:
+ _gpgrt_fputs_unlocked ("DBG: ", logstream);
+ length += 5;
+ break;
+ default:
+ rc = _gpgrt_fprintf_unlocked (logstream,
+ "[Unknown log level %d]: ", level);
+ if (rc > 0)
+ length += rc;
+ break;
+ }
+
+ return length;
+}
+
+
+/* Internal worker function. Exported so that we can use it in
+ * visibility.c. Returs the number of characters printed or 0 if the
+ * line ends in a LF. */
+int
+_gpgrt_logv_internal (int level, int ignore_arg_ptr, const char *extrastring,
+ const char *prefmt, const char *fmt, va_list arg_ptr)
+{
+ int leading_backspace = (fmt && *fmt == '\b');
+ int length;
+ int rc;
+
+ if (!logstream)
+ {
+#ifdef HAVE_W32_SYSTEM
+ char *tmp;
+
+ tmp = (no_registry
+ ? NULL
+ : _gpgrt_w32_reg_query_string (NULL, "Software\\\\GNU\\\\GnuPG",
+ "DefaultLogFile"));
+ _gpgrt_log_set_sink (tmp && *tmp? tmp : NULL, NULL, -1);
+ _gpgrt_free (tmp);
+#else
+ /* Make sure a log stream has been set. */
+ _gpgrt_log_set_sink (NULL, NULL, -1);
+#endif
+ if (!logstream)
+ {
+ fputs ("gpgrt fatal: failed to init log stream\n", stderr);
+ _gpgrt_abort ();
+ }
+ }
+
+ _gpgrt_flockfile (logstream);
+ if (missing_lf && level != GPGRT_LOGLVL_CONT)
+ _gpgrt_putc_unlocked ('\n', logstream );
+ missing_lf = 0;
+
+ length = print_prefix (level, leading_backspace);
+ if (leading_backspace)
+ fmt++;
+
+ if (fmt)
+ {
+ if (prefmt)
+ {
+ _gpgrt_fputs_unlocked (prefmt, logstream);
+ length += strlen (prefmt);
+ }
+
+ if (ignore_arg_ptr)
+ { /* This is used by log_string and comes with the extra
+ * feature that after a LF the next line is indent at the
+ * length of the prefix. Note that we do not yet include
+ * the length of the timestamp and pid in the indent
+ * computation. */
+ const char *p, *pend;
+
+ for (p = fmt; (pend = strchr (p, '\n')); p = pend+1)
+ {
+ rc = _gpgrt_fprintf_unlocked (logstream, "%*s%.*s",
+ (int)((p != fmt
+ && (with_prefix || force_prefixes))
+ ?strlen (prefix_buffer)+2:0), "",
+ (int)(pend - p)+1, p);
+ if (rc > 0)
+ length += rc;
+ }
+ _gpgrt_fputs_unlocked (p, logstream);
+ length += strlen (p);
+ }
+ else
+ {
+ struct fmt_string_filter_s sf = {NULL};
+
+ rc = _gpgrt_vfprintf_unlocked (logstream, fmt_string_filter, &sf,
+ fmt, arg_ptr);
+ if (rc > 0)
+ length += rc;
+ }
+
+ if (*fmt && fmt[strlen(fmt)-1] != '\n')
+ missing_lf = 1;
+ }
+
+ /* If we have an EXTRASTRING print it now while we still hold the
+ * lock on the logstream. */
+ if (extrastring)
+ {
+ int c;
+
+ if (missing_lf)
+ {
+ _gpgrt_putc_unlocked ('\n', logstream);
+ missing_lf = 0;
+ length = 0;
+ }
+ length += print_prefix (level, leading_backspace);
+ _gpgrt_fputs_unlocked (">> ", logstream);
+ length += 3;
+ missing_lf = 1;
+ while ((c = *extrastring++))
+ {
+ missing_lf = 1;
+ if (c == '\\')
+ {
+ _gpgrt_fputs_unlocked ("\\\\", logstream);
+ length += 2;
+ }
+ else if (c == '\r')
+ {
+ _gpgrt_fputs_unlocked ("\\r", logstream);
+ length += 2;
+ }
+ else if (c == '\n')
+ {
+ _gpgrt_fputs_unlocked ("\\n\n", logstream);
+ length = 0;
+ if (*extrastring)
+ {
+ length += print_prefix (level, leading_backspace);
+ _gpgrt_fputs_unlocked (">> ", logstream);
+ length += 3;
+ }
+ else
+ missing_lf = 0;
+ }
+ else
+ {
+ _gpgrt_putc_unlocked (c, logstream);
+ length++;
+ }
+ }
+ if (missing_lf)
+ {
+ _gpgrt_putc_unlocked ('\n', logstream);
+ length = 0;
+ missing_lf = 0;
+ }
+ }
+
+ if (level == GPGRT_LOGLVL_FATAL)
+ {
+ if (missing_lf)
+ _gpgrt_putc_unlocked ('\n', logstream);
+ _gpgrt_funlockfile (logstream);
+ exit (2);
+ }
+ else if (level == GPGRT_LOGLVL_BUG)
+ {
+ if (missing_lf)
+ _gpgrt_putc_unlocked ('\n', logstream );
+ _gpgrt_funlockfile (logstream);
+ /* Using backtrace requires a configure test and to pass
+ * -rdynamic to gcc. Thus we do not enable it now. */
+ /* { */
+ /* void *btbuf[20]; */
+ /* int btidx, btlen; */
+ /* char **btstr; */
+
+ /* btlen = backtrace (btbuf, DIM (btbuf)); */
+ /* btstr = backtrace_symbols (btbuf, btlen); */
+ /* if (btstr) */
+ /* for (btidx=0; btidx < btlen; btidx++) */
+ /* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */
+ /* } */
+ _gpgrt_abort ();
+ }
+ else
+ _gpgrt_funlockfile (logstream);
+
+ /* Bumb the error counter for log_error. */
+ if (level == GPGRT_LOGLVL_ERROR)
+ _gpgrt_inc_errorcount ();
+
+ return length;
+}
+
+
+void
+_gpgrt_log (int level, const char *fmt, ...)
+{
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt) ;
+ _gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+void
+_gpgrt_logv (int level, const char *fmt, va_list arg_ptr)
+{
+ _gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
+}
+
+
+/* Same as log_logv but PREFIX is printed immediately before FMT.
+ * Note that PREFIX is an additional string and independent of the
+ * prefix set by gpgrt_log_set_prefix. */
+void
+_gpgrt_logv_prefix (int level, const char *prefix,
+ const char *fmt, va_list arg_ptr)
+{
+ _gpgrt_logv_internal (level, 0, NULL, prefix, fmt, arg_ptr);
+}
+
+
+static void
+do_log_ignore_arg (int level, const char *str, ...)
+{
+ va_list arg_ptr;
+ va_start (arg_ptr, str);
+ _gpgrt_logv_internal (level, 1, NULL, NULL, str, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+/* Log STRING at LEVEL but indent from the second line on by the
+ * length of the prefix. */
+void
+_gpgrt_log_string (int level, const char *string)
+{
+ /* We need a dummy arg_ptr, but there is no portable way to create
+ * one. So we call the _gpgrt_logv_internal function through a
+ * variadic wrapper. */
+ do_log_ignore_arg (level, string);
+}
+
+
+void
+_gpgrt_log_info (const char *fmt, ...)
+{
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_INFO, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+void
+_gpgrt_log_error (const char *fmt, ...)
+{
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_ERROR, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+void
+_gpgrt_log_fatal (const char *fmt, ...)
+{
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_FATAL, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
+}
+
+
+void
+_gpgrt_log_bug (const char *fmt, ...)
+{
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_BUG, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
+}
+
+
+void
+_gpgrt_log_debug (const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+/* The same as log_debug but at the end of the output STRING is
+ * printed with LFs expanded to include the prefix and a final --end--
+ * marker. */
+void
+_gpgrt_log_debug_string (const char *string, const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, string, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+void
+_gpgrt_log_printf (const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_internal (fmt ? GPGRT_LOGLVL_CONT : GPGRT_LOGLVL_BEGIN,
+ 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+/* Flush the log - this is useful to make sure that the trailing
+ linefeed has been printed. */
+void
+_gpgrt_log_flush (void)
+{
+ do_log_ignore_arg (GPGRT_LOGLVL_CONT, NULL);
+}
+
+
+/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
+ * just the raw dump (in this case ARG_PTR is not used), with FMT
+ * being an empty string, print a trailing linefeed, otherwise print
+ * an entire debug line with the expanded FMT followed by a possible
+ * wrapped hexdump and a final LF. */
+void
+_gpgrt_logv_printhex (const void *buffer, size_t length,
+ const char *fmt, va_list arg_ptr)
+{
+ int wrap = 0;
+ int cnt = 0;
+ const unsigned char *p;
+
+ /* FIXME: This printing is not yet protected by _gpgrt_flockfile. */
+ if (fmt && *fmt)
+ {
+ _gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
+ wrap = 1;
+ }
+
+ if (length)
+ {
+ if (wrap)
+ _gpgrt_log_printf (" ");
+
+ for (p = buffer; length--; p++)
+ {
+ _gpgrt_log_printf ("%02x", *p);
+ if (wrap && ++cnt == 32 && length)
+ {
+ cnt = 0;
+ /* (we indicate continuations with a backslash) */
+ _gpgrt_log_printf (" \\\n");
+ _gpgrt_log_debug ("%s", "");
+ if (fmt && *fmt)
+ _gpgrt_log_printf (" ");
+ }
+ }
+ }
+
+ if (fmt)
+ _gpgrt_log_printf ("\n");
+}
+
+
+/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
+ * just the raw dump, with FMT being an empty string, print a trailing
+ * linefeed, otherwise print an entire debug line with the expanded
+ * FMT followed by the hexdump and a final LF. */
+void
+_gpgrt_log_printhex (const void *buffer, size_t length,
+ const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ if (fmt)
+ {
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_printhex (buffer, length, fmt, arg_ptr);
+ va_end (arg_ptr);
+ }
+ else
+ {
+ /* va_list is not necessary a pointer and thus we can't use NULL
+ * because that would conflict with platforms using a straight
+ * struct for it (e.g. arm64). We use a dummy variable instead;
+ * the static is a simple way zero it out so to not get
+ * complains about uninitialized use. */
+ static va_list dummy_argptr;
+
+ _gpgrt_logv_printhex (buffer, length, NULL, dummy_argptr);
+ }
+}
+
+
+/* Print a microsecond timestamp followed by FMT. */
+void
+_gpgrt_logv_clock (const char *fmt, va_list arg_ptr)
+{
+#if ENABLE_LOG_CLOCK
+ static unsigned long long initial;
+ struct timespec tv;
+ unsigned long long now;
+ char clockbuf[50];
+
+ if (clock_gettime (CLOCK_REALTIME, &tv))
+ {
+ _gpgrt_log_debug ("error getting the realtime clock value\n");
+ return;
+ }
+ now = tv.tv_sec * 1000000000ull;
+ now += tv.tv_nsec;
+
+ if (!initial)
+ initial = now;
+
+ snprintf (clockbuf, sizeof clockbuf, "[%6llu] ", (now - initial)/1000);
+ _gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, clockbuf, fmt, arg_ptr);
+
+#else /*!ENABLE_LOG_CLOCK*/
+
+ /* You may need to link with -ltr to use the above code. */
+
+ _gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG,
+ 0, NULL, "[no clock] ", fmt, arg_ptr);
+
+#endif /*!ENABLE_LOG_CLOCK*/
+}
+
+
+/* Print a microsecond timestamp followed by FMT. */
+void
+_gpgrt_log_clock (const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ _gpgrt_logv_clock (fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+void
+_gpgrt__log_assert (const char *expr, const char *file,
+ int line, const char *func)
+{
+#ifdef GPGRT_HAVE_MACRO_FUNCTION
+ _gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n",
+ expr, func, file, line);
+#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
+ _gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" failed (%s:%d)\n",
+ expr, file, line);
+#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
+}