summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will@willthompson.co.uk>2020-05-16 15:53:22 +0100
committerWill Thompson <will@willthompson.co.uk>2020-06-05 09:24:02 +0100
commit2b7de5136fbf9185f222de092afa99dc8e0ce1d7 (patch)
tree341bbba97f21b76a3d5461503168d0bbb6f289fc
parent3ac734ab948998e1386ac6f6738c68eeac9f03fd (diff)
Factor out BustlePcapReader
-rw-r--r--Makefile10
-rw-r--r--bustle.cabal4
-rw-r--r--c-sources/pcap-monitor.c75
-rw-r--r--c-sources/pcap-reader.c282
-rw-r--r--c-sources/pcap-reader.h43
5 files changed, 365 insertions, 49 deletions
diff --git a/Makefile b/Makefile
index 7a6370a..79b447e 100644
--- a/Makefile
+++ b/Makefile
@@ -24,9 +24,15 @@ SCALABLE_ICONS = \
all: $(BINARIES) $(MANPAGE) $(DESKTOP_FILE) $(APPDATA_FILE) $(SCALABLE_ICONS)
-BUSTLE_PCAP_SOURCES = c-sources/pcap-monitor.c c-sources/bustle-pcap.c
+BUSTLE_PCAP_SOURCES = \
+ c-sources/pcap-reader.c \
+ c-sources/pcap-monitor.c \
+ c-sources/bustle-pcap.c
BUSTLE_PCAP_GENERATED_HEADERS = dist/build/autogen/version.h
-BUSTLE_PCAP_HEADERS = c-sources/pcap-monitor.h $(BUSTLE_PCAP_GENERATED_HEADERS)
+BUSTLE_PCAP_HEADERS = \
+ c-sources/pcap-reader.h \
+ c-sources/pcap-monitor.h \
+ $(BUSTLE_PCAP_GENERATED_HEADERS)
bustle-pcap.1: dist/build/bustle-pcap
help2man --output=$@ --no-info --name='Generate D-Bus logs for bustle' $<
diff --git a/bustle.cabal b/bustle.cabal
index 544f681..569ad50 100644
--- a/bustle.cabal
+++ b/bustle.cabal
@@ -19,6 +19,7 @@ Build-type: Custom
Extra-source-files:
-- C bits
c-sources/bustle-pcap.c,
+ c-sources/pcap-reader.h,
c-sources/pcap-monitor.h,
c-sources/config.h,
Makefile,
@@ -109,7 +110,8 @@ Executable bustle
-fno-warn-unused-do-bind
if flag(threaded)
ghc-options: -threaded
- C-sources: c-sources/pcap-monitor.c
+ C-sources: c-sources/pcap-reader.c
+ , c-sources/pcap-monitor.c
cc-options: -fPIC -g
pkgconfig-depends: glib-2.0 >= 2.54,
gio-unix-2.0
diff --git a/c-sources/pcap-monitor.c b/c-sources/pcap-monitor.c
index 18b15e4..cb1d272 100644
--- a/c-sources/pcap-monitor.c
+++ b/c-sources/pcap-monitor.c
@@ -37,6 +37,8 @@
#include <glib/gstdio.h>
#include <gio/gunixinputstream.h>
+#include "pcap-reader.h"
+
/* Prefix of name claimed by the connection that collects name owners. */
const char *BUSTLE_MONITOR_NAME_PREFIX = "org.freedesktop.Bustle.Monitor.";
@@ -100,7 +102,7 @@ typedef struct _BustlePcapMonitor {
/* If >= 0, master side of controlling terminal for dbus_monitor */
int pt_master;
GSource *dbus_monitor_source;
- pcap_t *pcap_in;
+ BustlePcapReader *reader;
/* output */
gchar *filename;
@@ -230,7 +232,7 @@ bustle_pcap_monitor_dispose (GObject *object)
g_clear_object (&self->cancellable);
g_clear_pointer (&self->dbus_monitor_source, g_source_destroy);
- g_clear_pointer (&self->pcap_in, pcap_close);
+ g_clear_object (&self->reader);
g_clear_object (&self->dbus_monitor);
close_dump (self);
@@ -654,7 +656,6 @@ start_pcap (
GInputStream *stdout_pipe = NULL;
gint stdout_fd = -1;
FILE *dbus_monitor_filep = NULL;
- char errbuf[PCAP_ERRBUF_SIZE] = {0};
stdout_pipe = g_subprocess_get_stdout_pipe (self->dbus_monitor);
g_return_val_if_fail (stdout_pipe != NULL, FALSE);
@@ -676,24 +677,17 @@ start_pcap (
* fread(). It's safe to do this on the main thread, since we know the pipe
* is readable. On short read, pcap_fopen_offline() fails immediately.
*/
- self->pcap_in = pcap_fopen_offline (dbus_monitor_filep, errbuf);
- if (self->pcap_in == NULL)
+ self->reader = bustle_pcap_reader_fopen (g_steal_pointer (&dbus_monitor_filep), error);
+ if (self->reader == NULL)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Couldn't read messages from dbus-monitor: %s",
- errbuf);
-
- /* Cause dbus-monitor to exit next time it tries to write a message */
- g_clear_pointer (&dbus_monitor_filep, fclose);
+ g_prefix_error (error, "Couldn't read messages from dbus-monitor: ");
- /* And try to terminate it immediately. */
+ /* Try to terminate dbus-monitor immediately. The reader closes the FILE * on error. */
send_sigint (self);
return FALSE;
}
- /* pcap_close() will call fclose() on the FILE * passed to
- * pcap_fopen_offline() */
dump_names_async (self);
self->state = STATE_RUNNING;
return TRUE;
@@ -706,41 +700,30 @@ read_one (
{
struct pcap_pkthdr *hdr;
const guchar *blob;
- int ret;
g_autoptr(GDBusMessage) message = NULL;
- ret = pcap_next_ex (self->pcap_in, &hdr, &blob);
- switch (ret)
+ if (!bustle_pcap_reader_read_one (self->reader, &hdr, &blob, &message, error))
+ {
+ return FALSE;
+ }
+ else if (message == NULL)
{
- case 1:
- message = g_dbus_message_new_from_blob ((guchar *) blob, hdr->caplen, G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING, error);
- if (message == NULL)
- {
- g_prefix_error (error, "Error while parsing message from dbus-monitor: ");
- return FALSE;
- }
-
- g_signal_emit (self, signals[SIG_MESSAGE_LOGGED], 0,
- hdr->ts.tv_sec, hdr->ts.tv_usec, blob, hdr->caplen, message);
-
- /* cast necessary because pcap_dump has a type matching the callback
- * argument to pcap_loop()
- * TODO don't block
- */
- pcap_dump ((u_char *) self->dumper, hdr, blob);
- return TRUE;
-
- case -2:
- /* EOF; shouldn't happen since we waited for the FD to be readable */
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
- "EOF when reading from dbus-monitor");
- return FALSE;
+ /* EOF; shouldn't happen since we waited for the FD to be readable */
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
+ "EOF when reading from dbus-monitor");
+ return FALSE;
+ }
+ else
+ {
+ g_signal_emit (self, signals[SIG_MESSAGE_LOGGED], 0,
+ hdr->ts.tv_sec, hdr->ts.tv_usec, blob, hdr->caplen, message);
- default:
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Error %i reading dbus-monitor stream: %s",
- ret, pcap_geterr (self->pcap_in));
- return FALSE;
+ /* cast necessary because pcap_dump has a type matching the callback
+ * argument to pcap_loop()
+ * TODO don't block
+ */
+ pcap_dump ((u_char *) self->dumper, hdr, blob);
+ return TRUE;
}
}
@@ -834,7 +817,7 @@ cancellable_cancelled_cb (GCancellable *cancellable,
/* Closes the stream; should cause dbus-monitor to quit in due course when it
* tries to write to the other end of the pipe.
*/
- g_clear_pointer (&self->pcap_in, pcap_close);
+ bustle_pcap_reader_close (self->reader);
/* And try to terminate it immediately. */
send_sigint (self);
diff --git a/c-sources/pcap-reader.c b/c-sources/pcap-reader.c
new file mode 100644
index 0000000..7d3dd5f
--- /dev/null
+++ b/c-sources/pcap-reader.c
@@ -0,0 +1,282 @@
+/*
+ * pcap-reader.c - reads DBus messages from a pcap stream
+ * Copyright © 2011–2012 Collabora Ltd.
+ * Copyright © 2018–2020 Will Thompson
+ *
+ * This library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "pcap-reader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <pcap/pcap.h>
+
+typedef struct _BustlePcapReader {
+ GObject parent;
+
+ gchar *filename;
+ FILE *filep;
+
+ pcap_t *pcap_in;
+} BustlePcapReader;
+
+typedef enum {
+ PROP_FILENAME = 1,
+ PROP_FILEP
+} BustlePcapReaderProp;
+
+static void initable_iface_init (
+ gpointer g_class,
+ gpointer unused);
+
+G_DEFINE_TYPE_WITH_CODE (BustlePcapReader, bustle_pcap_reader, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init);
+ )
+
+/* A sad echo of the functions in libglnx. */
+static inline void *
+throw_errno (GError **error,
+ const gchar *prefix)
+{
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ "%s: %s", prefix, g_strerror (errsv));
+ return NULL;
+}
+
+static void
+bustle_pcap_reader_init (BustlePcapReader *self)
+{
+}
+
+static void
+bustle_pcap_reader_set_property (
+ GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BustlePcapReader *self = BUSTLE_PCAP_READER (object);
+
+ switch ((BustlePcapReaderProp) property_id)
+ {
+ case PROP_FILENAME:
+ self->filename = g_value_dup_string (value);
+ break;
+ case PROP_FILEP:
+ self->filep = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+bustle_pcap_reader_finalize (GObject *object)
+{
+ BustlePcapReader *self = BUSTLE_PCAP_READER (object);
+ GObjectClass *parent_class = bustle_pcap_reader_parent_class;
+
+ g_clear_pointer (&self->filename, g_free);
+ g_clear_pointer (&self->filep, fclose);
+ g_clear_pointer (&self->pcap_in, pcap_close);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static void
+bustle_pcap_reader_class_init (BustlePcapReaderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *param_spec;
+
+ object_class->set_property = bustle_pcap_reader_set_property;
+ object_class->finalize = bustle_pcap_reader_finalize;
+
+ param_spec = g_param_spec_string ("filename", "Filename",
+ "Path to pcap file to read",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
+
+ param_spec = g_param_spec_pointer ("filep", "FILE *",
+ "FILE * to read pcap stream from",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_FILEP, param_spec);
+}
+
+/**
+ * bustle_pcap_reader_read_one:
+ * @self:
+ * @hdr: (out) (transfer none): location to store pcap header (or %NULL on EOF)
+ * @blob: (out) (transfer none): location to store raw message (or %NULL on EOF)
+ * @message: (out) (transfer full): location to store parsed message (or %NULL on EOF)
+ * @error:
+ *
+ * Returns: %FALSE on error; %TRUE on success or end-of-file.
+ */
+gboolean
+bustle_pcap_reader_read_one (BustlePcapReader *self,
+ struct pcap_pkthdr **hdr,
+ const guchar **blob,
+ GDBusMessage **message,
+ GError **error)
+{
+ int ret;
+
+ g_return_val_if_fail (BUSTLE_IS_PCAP_READER (self), FALSE);
+ g_return_val_if_fail (hdr != NULL, FALSE);
+ g_return_val_if_fail (blob != NULL, FALSE);
+ g_return_val_if_fail (message != NULL && *message == NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (self->pcap_in == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Already closed");
+ return FALSE;
+ }
+
+ ret = pcap_next_ex (self->pcap_in, hdr, blob);
+ switch (ret)
+ {
+ case 1:
+ *message = g_dbus_message_new_from_blob ((guchar *) *blob, (*hdr)->caplen, G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING, error);
+ if (*message == NULL)
+ {
+ g_prefix_error (error, "Error while parsing message from dbus-monitor: ");
+ return FALSE;
+ }
+
+ return TRUE;
+
+ case -2:
+ /* EOF */
+ *hdr = NULL;
+ *blob = NULL;
+ *message = NULL;
+ return TRUE;
+
+ default:
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error %i reading dbus-monitor stream: %s",
+ ret, pcap_geterr (self->pcap_in));
+ return FALSE;
+ }
+}
+
+
+/**
+ * bustle_pcap_reader_close:
+ * @self: a #BustlePcapReader
+ *
+ * Closes the underlying file or stream.
+ *
+ * If @self is reading from a pipe to `dbus-monitor`, this will cause
+ * `dbus-monitor` to quit in due course when it next tries to write to the
+ * pipe.
+ */
+void
+bustle_pcap_reader_close (BustlePcapReader *self)
+{
+ g_return_if_fail (BUSTLE_IS_PCAP_READER (self));
+
+ g_clear_pointer (&self->pcap_in, pcap_close);
+ g_clear_pointer (&self->filep, fclose);
+}
+
+static gboolean
+initable_init (
+ GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ BustlePcapReader *self = BUSTLE_PCAP_READER (initable);
+ char errbuf[PCAP_ERRBUF_SIZE] = {0};
+
+ g_return_val_if_fail ((self->filename == NULL) ^ (self->filep == NULL),
+ FALSE);
+
+ if (self->filename != NULL)
+ {
+ self->pcap_in = pcap_open_offline (self->filename, errbuf);
+ }
+ else /* self->filep != NULL */
+ {
+ self->pcap_in = pcap_fopen_offline (self->filep, errbuf);
+ }
+
+ if (self->pcap_in == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, errbuf);
+ return FALSE;
+ }
+
+ /* Now owned by pcap_in */
+ self->filep = NULL;
+ return TRUE;
+}
+
+static void
+initable_iface_init (
+ gpointer g_class,
+ gpointer unused)
+{
+ GInitableIface *iface = g_class;
+
+ iface->init = initable_init;
+}
+
+BustlePcapReader *
+bustle_pcap_reader_open (const gchar *filename,
+ GError **error)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_initable_new (
+ BUSTLE_TYPE_PCAP_READER, NULL, error,
+ "filename", filename,
+ NULL);
+}
+
+/**
+ * bustle_pcap_reader_fopen:
+ * @filep: (transfer full):
+ *
+ * Returns: a reader, or %NULL on error
+ */
+BustlePcapReader *
+bustle_pcap_reader_fopen (FILE *filep,
+ GError **error)
+{
+ g_return_val_if_fail (filep != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_initable_new (
+ BUSTLE_TYPE_PCAP_READER, NULL, error,
+ "filep", filep,
+ NULL);
+}
diff --git a/c-sources/pcap-reader.h b/c-sources/pcap-reader.h
new file mode 100644
index 0000000..e8c1a16
--- /dev/null
+++ b/c-sources/pcap-reader.h
@@ -0,0 +1,43 @@
+/*
+ * pcap-reader.h - reads DBus messages from a pcap stream
+ * Copyright © 2011–2012 Collabora Ltd.
+ * Copyright © 2018–2020 Will Thompson
+ *
+ * This library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <pcap/pcap.h>
+
+#define BUSTLE_TYPE_PCAP_READER bustle_pcap_reader_get_type ()
+G_DECLARE_FINAL_TYPE (BustlePcapReader, bustle_pcap_reader, BUSTLE, PCAP_READER, GObject)
+
+BustlePcapReader *bustle_pcap_reader_open (const gchar *filename,
+ GError **error);
+BustlePcapReader *bustle_pcap_reader_fopen (FILE *filep,
+ GError **error);
+BustlePcapReader *bustle_pcap_reader_new (const gchar *filename,
+ GError **error);
+gboolean bustle_pcap_reader_read_one (BustlePcapReader *self,
+ struct pcap_pkthdr **hdr,
+ const guchar **blob,
+ GDBusMessage **message,
+ GError **error);
+void bustle_pcap_reader_close (BustlePcapReader *self);
+