From 2b7de5136fbf9185f222de092afa99dc8e0ce1d7 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 16 May 2020 15:53:22 +0100 Subject: Factor out BustlePcapReader --- Makefile | 10 +- bustle.cabal | 4 +- c-sources/pcap-monitor.c | 75 +++++-------- c-sources/pcap-reader.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++ c-sources/pcap-reader.h | 43 ++++++++ 5 files changed, 365 insertions(+), 49 deletions(-) create mode 100644 c-sources/pcap-reader.c create mode 100644 c-sources/pcap-reader.h 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 #include +#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 +#include +#include +#include +#include +#include +#include + +#include + +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 +#include +#include +#include + +#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); + -- cgit v1.2.3