[glib] Drop libsystemd dependency



commit 6a07885a98b89f9f1bb627c5996ec3f6e74b5bb5
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jul 22 22:56:26 2016 -0400

    Drop libsystemd dependency
    
    Talk to the journal ourselves using sendmsg() instead of linking
    against libsystemd for sd_journal_sendv(). At the same time, we
    can also avoid excessive copying.
    
    The motivation for dropping the dependency is that we can
    then use structured logging e.g. in a flatpak sandbox where
    libsystemd may not be present in the runtime.
    
    The code here is inspired by similar code in libvirt.

 configure.ac     |   15 ----
 glib/gmessages.c |  195 ++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 154 insertions(+), 56 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 86e5e05..a378cf2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1730,21 +1730,6 @@ if test x$have_libelf = xyes; then
   AC_DEFINE(HAVE_LIBELF, 1, [Define if libelf is available])
 fi
 
-dnl *************************
-dnl *** check for systemd ***
-dnl *************************
-AC_ARG_ENABLE([libsystemd],
-              [AS_HELP_STRING([--disable-libsystemd],
-                              [build without libsystemd support])])
-AS_IF([test "$enable_libsystemd" != "no"],[
-  PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd],
-                    [have_libsystemd=yes], [have_libsystemd=no])
-])
-
-AS_IF([test "$have_libsystemd" = "yes"],[
-  AC_DEFINE([HAVE_LIBSYSTEMD],[1],[Define if libsystemd is available])
-])
-
 dnl ****************************************
 dnl *** platform dependent source checks ***
 dnl ****************************************
diff --git a/glib/gmessages.c b/glib/gmessages.c
index fc21574..a16db58 100644
--- a/glib/gmessages.c
+++ b/glib/gmessages.c
@@ -104,6 +104,8 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <fcntl.h>
+#include <sys/uio.h>
 
 #include "glib-init.h"
 #include "galloca.h"
@@ -130,12 +132,6 @@
 #  include <windows.h>
 #endif
 
-#ifdef HAVE_LIBSYSTEMD
-#define SD_JOURNAL_SUPPRESS_LOCATION 1
-#include <sys/uio.h>
-#include <systemd/sd-journal.h>
-#endif
-
 
 /**
  * SECTION:messages
@@ -1475,7 +1471,7 @@ g_log_structured (const gchar    *log_domain,
 
   fields[1].key = "PRIORITY";
   fields[1].value = log_level_to_priority (log_level);
-  fields[1].length = 1;
+  fields[1].length = -1;
 
   if (log_domain)
     {
@@ -1623,6 +1619,21 @@ g_log_writer_supports_color (gint output_fd)
   return isatty (output_fd);
 }
 
+static int journal_fd = -1;
+
+static void
+open_journal (void)
+{
+  if ((journal_fd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
+    return;
+
+  if (fcntl (journal_fd, F_SETFD, FD_CLOEXEC) < 0)
+    {
+      close (journal_fd);
+      journal_fd = -1;
+    }
+}
+
 /**
  * g_log_writer_is_journald:
  * @output_fd: output file descriptor to check
@@ -1637,7 +1648,6 @@ g_log_writer_supports_color (gint output_fd)
 gboolean
 g_log_writer_is_journald (gint output_fd)
 {
-#ifdef HAVE_LIBSYSTEMD
   /* FIXME: Use the new journal API for detecting whether we’re writing to the
    * journal. See: https://github.com/systemd/systemd/issues/2473
    */
@@ -1654,13 +1664,14 @@ g_log_writer_is_journald (gint output_fd)
       if (err == 0 && addr.ss_family == AF_UNIX)
         fd_is_journal = g_str_has_prefix (((struct sockaddr_un *)&addr)->sun_path,
                                           "/run/systemd/journal/");
+
+      if (fd_is_journal)
+        open_journal ();
+
       g_once_init_leave (&initialized, TRUE);
     }
 
   return fd_is_journal;
-#else /* if !HAVE_LIBSYSTEMD */
-  return FALSE;
-#endif
 }
 
 static void escape_string (GString *string);
@@ -1771,6 +1782,78 @@ g_log_writer_format_fields (GLogLevelFlags   log_level,
   return g_string_free (gstring, FALSE);
 }
 
+static int
+journal_sendv (struct iovec *iov,
+               gsize         iovlen)
+{
+  int buf_fd = -1;
+  struct msghdr mh;
+  struct sockaddr_un sa;
+  union {
+    struct cmsghdr cmsghdr;
+    guint8 buf[CMSG_SPACE(sizeof(int))];
+  } control;
+  struct cmsghdr *cmsg;
+  char path[] = "/dev/shm/journal.XXXXXX";
+
+  if (journal_fd < 0)
+    return -1;
+
+  memset (&sa, 0, sizeof (sa));
+  sa.sun_family = AF_UNIX;
+  if (g_strlcpy (sa.sun_path, "/run/systemd/journal/socket", sizeof (sa.sun_path)) >= sizeof (sa.sun_path))
+    return -1;
+
+  memset (&mh, 0, sizeof (mh));
+  mh.msg_name = &sa;
+  mh.msg_namelen = offsetof (struct sockaddr_un, sun_path) + strlen (sa.sun_path);
+  mh.msg_iov = iov;
+  mh.msg_iovlen = iovlen;
+
+  if (sendmsg (journal_fd, &mh, MSG_NOSIGNAL) >= 0)
+    return 0;
+
+  if (errno != EMSGSIZE && errno != ENOBUFS)
+    return -1;
+
+  /* Message was too large, so dump to temporary file
+   * and pass an FD to the journal
+   */
+  if ((buf_fd = mkostemp (path, O_CLOEXEC|O_RDWR)) < 0)
+    return -1;
+
+  if (unlink (path) < 0)
+    {
+      close (buf_fd);
+      return -1;
+    }
+
+  if (writev (buf_fd, iov, iovlen) < 0)
+    {
+      close (buf_fd);
+      return -1;
+    }
+
+  mh.msg_iov = NULL;
+  mh.msg_iovlen = 0;
+
+  memset (&control, 0, sizeof (control));
+  mh.msg_control = &control;
+  mh.msg_controllen = sizeof (control);
+
+  cmsg = CMSG_FIRSTHDR (&mh);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+  memcpy (CMSG_DATA (cmsg), &buf_fd, sizeof (int));
+
+  mh.msg_controllen = cmsg->cmsg_len;
+
+  (void) sendmsg (journal_fd, &mh, MSG_NOSIGNAL);
+
+  return 0;
+}
+
 /**
  * g_log_writer_journald:
  * @log_level: log level, either from #GLogLevelFlags, or a user-defined
@@ -1799,9 +1882,11 @@ g_log_writer_journald (GLogLevelFlags   log_level,
                        gsize            n_fields,
                        gpointer         user_data)
 {
-#ifdef HAVE_LIBSYSTEMD
-  gsize i;
-  struct iovec *pairs;
+  const char equals = '=';
+  const char newline = '\n';
+  gsize i, k;
+  struct iovec *iov, *v;
+  char *buf;
   gint retval;
 
   g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED);
@@ -1814,38 +1899,66 @@ g_log_writer_journald (GLogLevelFlags   log_level,
    * locale’s character set.
    */
 
-  pairs = g_alloca (sizeof (struct iovec) * n_fields);
+  iov = g_alloca (sizeof (struct iovec) * 5 * n_fields);
+  buf = g_alloca (32 * n_fields);
 
+  k = 0;
+  v = iov;
   for (i = 0; i < n_fields; i++)
     {
-      guint8 *buf = NULL;
-      gsize key_length;
-      gsize value_length;
-
-      /* Build the iovec for this field. */
-      key_length = strlen (fields[i].key);
-      value_length =
-          (fields[i].length < 0) ? strlen (fields[i].value) : fields[i].length;
-
-      buf = g_malloc (key_length + 1 + value_length + 1);
-      pairs[i].iov_base = buf;
-      pairs[i].iov_len = key_length + 1 + value_length;
-
-      strncpy ((char *) buf, fields[i].key, key_length);
-      buf[key_length] = '=';
-      memcpy ((char *) buf + key_length + 1, fields[i].value, value_length);
-      buf[key_length + 1 + value_length] = '\0';
-    }
+      gsize length;
+      gboolean binary;
 
-  retval = sd_journal_sendv (pairs, n_fields);
+      if (fields[i].length < 0)
+        {
+          length = strlen (fields[i].value);
+          binary = strchr (fields[i].value, '\n') != NULL;
+        }
+      else
+        {
+          length = fields[i].length;
+          binary = TRUE;
+        }
 
-  for (i = 0; i < n_fields; i++)
-    g_free (pairs[i].iov_base);
+      if (binary)
+        {
+          guint64 nstr;
 
-  return (retval == 0) ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
-#else /* if !HAVE_LIBSYSTEMD */
-  return G_LOG_WRITER_UNHANDLED;
-#endif
+          v[0].iov_base = (gpointer)fields[i].key;
+          v[0].iov_len = strlen (fields[i].key);
+
+          v[1].iov_base = (gpointer)&newline;
+          v[1].iov_len = 1;
+
+          nstr = htole64 (length);
+          memcpy (&buf[k], &nstr, sizeof (nstr));
+
+          v[2].iov_base = &buf[k];
+          v[2].iov_len = sizeof (nstr);
+          v += 3;
+          k += sizeof (nstr);
+        }
+      else
+        {
+          v[0].iov_base = (gpointer)fields[i].key;
+          v[0].iov_len = strlen (fields[i].key);
+
+          v[1].iov_base = (gpointer)&equals;
+          v[1].iov_len = 1;
+          v += 2;
+        }
+
+      v[0].iov_base = (gpointer)fields[i].value;
+      v[0].iov_len = length;
+
+      v[1].iov_base = (gpointer)&newline;
+      v[1].iov_len = 1;
+      v += 2;
+    }
+
+  retval = journal_sendv (iov, v - iov);
+
+  return retval == 0 ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
 }
 
 /**
@@ -2414,7 +2527,7 @@ g_log_default_handler (const gchar   *log_domain,
 
   fields[2].key = "PRIORITY";
   fields[2].value = log_level_to_priority (log_level);
-  fields[2].length = 1;
+  fields[2].length = -1;
   n_fields++;
 
   if (log_domain)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]