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.
This commit is contained in:
Matthias Clasen 2016-07-22 22:56:26 -04:00
parent ca775518d8
commit 6a07885a98
2 changed files with 151 additions and 53 deletions

View File

@ -1730,21 +1730,6 @@ if test x$have_libelf = xyes; then
AC_DEFINE(HAVE_LIBELF, 1, [Define if libelf is available]) AC_DEFINE(HAVE_LIBELF, 1, [Define if libelf is available])
fi 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 ****************************************
dnl *** platform dependent source checks *** dnl *** platform dependent source checks ***
dnl **************************************** dnl ****************************************

View File

@ -104,6 +104,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <fcntl.h>
#include <sys/uio.h>
#include "glib-init.h" #include "glib-init.h"
#include "galloca.h" #include "galloca.h"
@ -130,12 +132,6 @@
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef HAVE_LIBSYSTEMD
#define SD_JOURNAL_SUPPRESS_LOCATION 1
#include <sys/uio.h>
#include <systemd/sd-journal.h>
#endif
/** /**
* SECTION:messages * SECTION:messages
@ -1475,7 +1471,7 @@ g_log_structured (const gchar *log_domain,
fields[1].key = "PRIORITY"; fields[1].key = "PRIORITY";
fields[1].value = log_level_to_priority (log_level); fields[1].value = log_level_to_priority (log_level);
fields[1].length = 1; fields[1].length = -1;
if (log_domain) if (log_domain)
{ {
@ -1623,6 +1619,21 @@ g_log_writer_supports_color (gint output_fd)
return isatty (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: * g_log_writer_is_journald:
* @output_fd: output file descriptor to check * @output_fd: output file descriptor to check
@ -1637,7 +1648,6 @@ g_log_writer_supports_color (gint output_fd)
gboolean gboolean
g_log_writer_is_journald (gint output_fd) g_log_writer_is_journald (gint output_fd)
{ {
#ifdef HAVE_LIBSYSTEMD
/* FIXME: Use the new journal API for detecting whether were writing to the /* FIXME: Use the new journal API for detecting whether were writing to the
* journal. See: https://github.com/systemd/systemd/issues/2473 * 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) if (err == 0 && addr.ss_family == AF_UNIX)
fd_is_journal = g_str_has_prefix (((struct sockaddr_un *)&addr)->sun_path, fd_is_journal = g_str_has_prefix (((struct sockaddr_un *)&addr)->sun_path,
"/run/systemd/journal/"); "/run/systemd/journal/");
if (fd_is_journal)
open_journal ();
g_once_init_leave (&initialized, TRUE); g_once_init_leave (&initialized, TRUE);
} }
return fd_is_journal; return fd_is_journal;
#else /* if !HAVE_LIBSYSTEMD */
return FALSE;
#endif
} }
static void escape_string (GString *string); static void escape_string (GString *string);
@ -1771,6 +1782,78 @@ g_log_writer_format_fields (GLogLevelFlags log_level,
return g_string_free (gstring, FALSE); 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: * g_log_writer_journald:
* @log_level: log level, either from #GLogLevelFlags, or a user-defined * @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, gsize n_fields,
gpointer user_data) gpointer user_data)
{ {
#ifdef HAVE_LIBSYSTEMD const char equals = '=';
gsize i; const char newline = '\n';
struct iovec *pairs; gsize i, k;
struct iovec *iov, *v;
char *buf;
gint retval; gint retval;
g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED); g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED);
@ -1814,38 +1899,66 @@ g_log_writer_journald (GLogLevelFlags log_level,
* locales character set. * locales 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++) for (i = 0; i < n_fields; i++)
{ {
guint8 *buf = NULL; gsize length;
gsize key_length; gboolean binary;
gsize value_length;
/* Build the iovec for this field. */ if (fields[i].length < 0)
key_length = strlen (fields[i].key); {
value_length = length = strlen (fields[i].value);
(fields[i].length < 0) ? strlen (fields[i].value) : fields[i].length; binary = strchr (fields[i].value, '\n') != NULL;
}
else
{
length = fields[i].length;
binary = TRUE;
}
buf = g_malloc (key_length + 1 + value_length + 1); if (binary)
pairs[i].iov_base = buf; {
pairs[i].iov_len = key_length + 1 + value_length; guint64 nstr;
strncpy ((char *) buf, fields[i].key, key_length); v[0].iov_base = (gpointer)fields[i].key;
buf[key_length] = '='; v[0].iov_len = strlen (fields[i].key);
memcpy ((char *) buf + key_length + 1, fields[i].value, value_length);
buf[key_length + 1 + value_length] = '\0'; 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 = sd_journal_sendv (pairs, n_fields); retval = journal_sendv (iov, v - iov);
for (i = 0; i < n_fields; i++) return retval == 0 ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
g_free (pairs[i].iov_base);
return (retval == 0) ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
#else /* if !HAVE_LIBSYSTEMD */
return G_LOG_WRITER_UNHANDLED;
#endif
} }
/** /**
@ -2414,7 +2527,7 @@ g_log_default_handler (const gchar *log_domain,
fields[2].key = "PRIORITY"; fields[2].key = "PRIORITY";
fields[2].value = log_level_to_priority (log_level); fields[2].value = log_level_to_priority (log_level);
fields[2].length = 1; fields[2].length = -1;
n_fields++; n_fields++;
if (log_domain) if (log_domain)