summary refs log tree commit diff stats
path: root/libotr/libgpg-error-1.42/src/logging.c
diff options
context:
space:
mode:
Diffstat (limited to 'libotr/libgpg-error-1.42/src/logging.c')
-rw-r--r--libotr/libgpg-error-1.42/src/logging.c1341
1 files changed, 1341 insertions, 0 deletions
diff --git a/libotr/libgpg-error-1.42/src/logging.c b/libotr/libgpg-error-1.42/src/logging.c
new file mode 100644
index 0000000..e4b7e40
--- /dev/null
+++ b/libotr/libgpg-error-1.42/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.  */
+}