From 0a269e15bfc91b310bc7571934e632434a3f07fa Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 14 Feb 2013 17:23:54 +0000 Subject: [PATCH 5/6] Add support for piping the message stream (in pcap format) to a command This can be used to record a message stream for Bustle. It's normally no better than Bustle's own bustle-pcap utility, but it does have the advantage that it can't miss messages if you need to start capturing messages as soon as the dbus-daemon starts running. Signed-off-by: Simon McVittie --- bus/Makefile.am | 2 + bus/bus.c | 15 ++ bus/bus.h | 7 + bus/capture.c | 211 +++++++++++++++++++++++++++ bus/capture.h | 56 +++++++ bus/config-parser-common.c | 6 + bus/config-parser-common.h | 1 + bus/config-parser.c | 53 +++++++ bus/config-parser.h | 2 + bus/connection.c | 49 ++++++- bus/connection.h | 6 + bus/dispatch.c | 6 + bus/session.conf.in | 5 + bus/system.conf.in | 5 + configure.ac | 9 ++ test/name-test/tmp-session-like-system.conf | 2 + 16 files changed, 434 insertions(+), 1 deletion(-) create mode 100644 bus/capture.c create mode 100644 bus/capture.h diff --git a/bus/Makefile.am b/bus/Makefile.am index 6cbc09a..b0d9f97 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -69,6 +69,8 @@ BUS_SOURCES= \ activation-exit-codes.h \ bus.c \ bus.h \ + capture.c \ + capture.h \ config-parser.c \ config-parser.h \ config-parser-common.c \ diff --git a/bus/bus.c b/bus/bus.c index e80e708..757ae5c 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -27,6 +27,7 @@ #include #include "activation.h" +#include "capture.h" #include "connection.h" #include "services.h" #include "utils.h" @@ -63,6 +64,7 @@ struct BusContext BusPolicy *policy; BusMatchmaker *matchmaker; BusLimits limits; + BusCapture capture; unsigned int fork : 1; unsigned int syslog : 1; unsigned int keep_umask : 1; @@ -668,6 +670,8 @@ process_config_postinit (BusContext *context, DBusHashTable *service_context_table; DBusList *watched_dirs = NULL; + bus_capture_maybe_start (&context->capture, context, parser); + raise_file_descriptor_limit (context); service_context_table = bus_config_parser_steal_service_context_table (parser); @@ -1018,6 +1022,8 @@ bus_context_shutdown (BusContext *context) { DBusList *link; + bus_capture_stop (&context->capture, context); + link = _dbus_list_get_first_link (&context->servers); while (link != NULL) { @@ -1646,3 +1652,12 @@ bus_context_check_security_policy (BusContext *context, _dbus_verbose ("security policy allowing message\n"); return TRUE; } + +#ifdef DBUS_ENABLE_CAPTURE +void +bus_context_capture_each (BusContext *self, + DBusList **messages) +{ + bus_capture_capture_each (&self->capture, self, messages); +} +#endif diff --git a/bus/bus.h b/bus/bus.h index 3597884..060a783 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -126,4 +126,11 @@ dbus_bool_t bus_context_check_security_policy (BusContext DBusMessage *message, DBusError *error); +void bus_context_capture_each (BusContext *self, + DBusList **messages); + +#ifndef DBUS_ENABLE_CAPTURE +#define bus_context_capture_each(s, m) do { } while (0) +#endif + #endif /* BUS_BUS_H */ diff --git a/bus/capture.c b/bus/capture.c new file mode 100644 index 0000000..f15a7c3 --- /dev/null +++ b/bus/capture.c @@ -0,0 +1,211 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* capture.c - capture traffic in pcap format + * + * Copyright © 2012 Intel Corporation + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "capture.h" + +#ifdef DBUS_ENABLE_CAPTURE + +#ifndef DBUS_UNIX +/* We use popen(), because life's too short to reimplement it... */ +#error --enable-capture is only implemented on Unix at the moment +#endif + +#include + +/* The pcap file format is pretty simple. */ +typedef struct { + dbus_uint32_t magic_number; + dbus_uint16_t version_major; + dbus_uint16_t version_minor; + dbus_int32_t thiszone; + dbus_uint32_t sigfigs; + dbus_uint32_t snaplen; + dbus_uint32_t network; +} pcap_hdr_t; +/* That struct deliberately uses natural alignment, and is assumed to be + * packed. If this assertion fails, you'll have to serialize it "properly". */ +_DBUS_STATIC_ASSERT (sizeof (pcap_hdr_t) == 24); + +void +bus_capture_maybe_start (BusCapture *self, + BusContext *context, + BusConfigParser *parser) +{ + pcap_hdr_t header = { + 0xA1B2C3D4, /* magic number and endianness indicator */ + 2, 4, /* pcap format version 2.4 */ + 0, /* UTC */ + 0, /* documented as "ignored in practice" */ + DBUS_MAXIMUM_MESSAGE_LENGTH, /* we do not currently truncate messages */ + 231 /* LINKTYPE_DBUS on http://www.tcpdump.org/linktypes.html */ + }; + const char *command; + + /* ignore if already capturing */ + if (self->pcap != NULL) + return; + + command = bus_config_parser_get_pcap_recipient (parser); + + if (command == NULL || *command == '\0') + return; + + /* For simplicity, we use stdio to perform blocking I/O. */ + self->pcap = popen (command, "w"); + + if (self->pcap == NULL) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to open pipe to pcap recipient '%s': %s", + command, _dbus_strerror (errno)); + return; + } + + _dbus_fd_set_close_on_exec (fileno (self->pcap)); + + if (fwrite (&header, sizeof (header), 1, self->pcap) != 1) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to write pcap header: %s", + _dbus_strerror (errno)); + bus_capture_stop (self, context); + return; + } +} + +void +bus_capture_stop (BusCapture *self, + BusContext *context) +{ + if (self->pcap != NULL && pclose (self->pcap) < 0) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to close pipe to pcap recipient"); + } + + self->pcap = NULL; +} + +void +bus_capture_capture (BusCapture *self, + BusContext *context, + DBusMessage *message) +{ + char *blob; + long tv_sec, tv_usec; + dbus_uint32_t packet_header[4]; + int len; + + if (self->pcap == NULL) + return; + + _dbus_assert (message != NULL); + + if (dbus_message_marshal (message, &blob, &len)) + { + _dbus_assert (len >= DBUS_MINIMUM_HEADER_SIZE); + _dbus_assert (len <= DBUS_MAXIMUM_MESSAGE_LENGTH); + + /* We use the real time, because that's what will align with syslog. */ + _dbus_get_real_time (&tv_sec, &tv_usec); + + packet_header[0] = tv_sec; + packet_header[1] = tv_usec; + packet_header[2] = len; + packet_header[3] = len; /* would be < len if truncated */ + + if (fwrite (packet_header, sizeof (dbus_uint32_t), 4, + self->pcap) != 4) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to write packet header to pcap recipient: %s", + _dbus_strerror (errno)); + bus_capture_stop (self, context); + dbus_free (blob); + return; + } + + if (fwrite (blob, len, 1, self->pcap) != 1) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to write packet to pcap recipient: %s", + _dbus_strerror (errno)); + bus_capture_stop (self, context); + dbus_free (blob); + return; + } + + dbus_free (blob); + + if (fflush (self->pcap) != 0) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "unable to flush pcap pipe: %s", + _dbus_strerror (errno)); + /* might as well carry on and hope for the best... */ + return; + } + } + else + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "not enough memory to marshal message for pcap recipient"); + return; + } +} + +void +bus_capture_capture_each (BusCapture *self, + BusContext *context, + DBusList **messages) +{ + DBusList *link; + + for (link = _dbus_list_get_first_link (messages); + link != NULL; + link = _dbus_list_get_next_link (messages, link)) + { + bus_capture_capture (self, context, link->data); + } +} + +#else /* !DBUS_ENABLE_CAPTURE */ + +void +bus_capture_maybe_start (BusCapture *self, + BusContext *context, + BusConfigParser *parser) +{ + const char *command; + + command = bus_config_parser_get_pcap_recipient (parser); + + if (command != NULL && *command != '\0') + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "packet capture not enabled, recompile with --enable-capture to use "); + } +} + +#endif diff --git a/bus/capture.h b/bus/capture.h new file mode 100644 index 0000000..7f01cd9 --- /dev/null +++ b/bus/capture.h @@ -0,0 +1,56 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* capture.h - capture traffic in pcap format + * + * Copyright © 2012 Intel Corporation + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BUS_CAPTURE_H +#define BUS_CAPTURE_H + +#include + +#include + +#include "config-parser.h" + +typedef struct { + /**/ + FILE *pcap; +} BusCapture; + +void bus_capture_maybe_start (BusCapture *self, + BusContext *context, + BusConfigParser *parser); +void bus_capture_stop (BusCapture *self, + BusContext *context); +void bus_capture_capture (BusCapture *self, + BusContext *context, + DBusMessage *message); +void bus_capture_capture_each (BusCapture *self, + BusContext *context, + DBusList **messages); + +#ifndef DBUS_ENABLE_CAPTURE +#define bus_capture_stop(s, c) do { } while (0) +#define bus_capture_capture(s, c, m) do { } while (0) +#define bus_capture_capture_each(s, c, m) do { } while (0) +#endif + +#endif diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c index c522ff4..b064f12 100644 --- a/bus/config-parser-common.c +++ b/bus/config-parser-common.c @@ -127,6 +127,10 @@ bus_config_parser_element_name_to_type (const char *name) { return ELEMENT_ALLOW_ANONYMOUS; } + else if (strcmp (name, "pcap_recipient") == 0) + { + return ELEMENT_PCAP_RECIPIENT; + } return ELEMENT_NONE; } @@ -181,6 +185,8 @@ bus_config_parser_element_type_to_name (ElementType type) return "keep_umask"; case ELEMENT_ALLOW_ANONYMOUS: return "allow_anonymous"; + case ELEMENT_PCAP_RECIPIENT: + return "pcap_recipient"; } _dbus_assert_not_reached ("bad element type"); diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h index 186bf4c..850d6e8 100644 --- a/bus/config-parser-common.h +++ b/bus/config-parser-common.h @@ -49,6 +49,7 @@ typedef enum ELEMENT_STANDARD_SYSTEM_SERVICEDIRS, ELEMENT_KEEP_UMASK, ELEMENT_SYSLOG, + ELEMENT_PCAP_RECIPIENT, ELEMENT_ALLOW_ANONYMOUS } ElementType; diff --git a/bus/config-parser.c b/bus/config-parser.c index 1d1b8bf..8f26de4 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -112,6 +112,8 @@ struct BusConfigParser DBusHashTable *service_context_table; /**< Map service names to SELinux contexts */ + char *pcap_recipient; /**< shell command line to capture a pcap trace */ + unsigned int fork : 1; /**< TRUE to fork into daemon mode */ unsigned int syslog : 1; /**< TRUE to enable syslog */ @@ -497,6 +499,7 @@ bus_config_parser_unref (BusConfigParser *parser) dbus_free (parser->servicehelper); dbus_free (parser->bus_type); dbus_free (parser->pidfile); + dbus_free (parser->pcap_recipient); _dbus_list_foreach (&parser->listen_on, (DBusForeachFunction) dbus_free, @@ -1127,6 +1130,19 @@ start_busconfig_child (BusConfigParser *parser, return TRUE; } + else if (element_type == ELEMENT_PCAP_RECIPIENT) + { + if (!check_no_attributes (parser, "pcap_recipient", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_PCAP_RECIPIENT) == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + return TRUE; + } else { dbus_set_error (error, DBUS_ERROR_FAILED, @@ -2031,6 +2047,7 @@ bus_config_parser_end_element (BusConfigParser *parser, case ELEMENT_SERVICEHELPER: case ELEMENT_INCLUDEDIR: case ELEMENT_LIMIT: + case ELEMENT_PCAP_RECIPIENT: if (!e->had_content) { dbus_set_error (error, DBUS_ERROR_FAILED, @@ -2308,6 +2325,27 @@ include_dir (BusConfigParser *parser, return retval; } +static dbus_bool_t +pcap_recipient (BusConfigParser *parser, + const DBusString *command, + DBusError *error) +{ + char *command_str; + + command_str = _dbus_strdup (_dbus_string_get_const_data (command)); + + if (command_str == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + dbus_free (parser->pcap_recipient); + parser->pcap_recipient = command_str; + + return TRUE; +} + dbus_bool_t bus_config_parser_content (BusConfigParser *parser, const DBusString *content, @@ -2599,6 +2637,15 @@ bus_config_parser_content (BusConfigParser *parser, e->d.limit.name); } break; + + case ELEMENT_PCAP_RECIPIENT: + { + e->had_content = TRUE; + + if (!pcap_recipient (parser, content, error)) + return FALSE; + } + break; } _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -2706,6 +2753,12 @@ bus_config_parser_get_servicehelper (BusConfigParser *parser) return parser->servicehelper; } +const char * +bus_config_parser_get_pcap_recipient (BusConfigParser *parser) +{ + return parser->pcap_recipient; +} + BusPolicy* bus_config_parser_steal_policy (BusConfigParser *parser) { diff --git a/bus/config-parser.h b/bus/config-parser.h index ba5bf74..7e442bf 100644 --- a/bus/config-parser.h +++ b/bus/config-parser.h @@ -74,6 +74,8 @@ BusPolicy* bus_config_parser_steal_policy (BusConfigParser *parser); void bus_config_parser_get_limits (BusConfigParser *parser, BusLimits *limits); +const char *bus_config_parser_get_pcap_recipient (BusConfigParser *parser); + DBusHashTable* bus_config_parser_steal_service_context_table (BusConfigParser *parser); /* Loader functions (backended off one of the XML parsers). Returns a diff --git a/bus/connection.c b/bus/connection.c index 4b3d1e7..d0b5eac 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -1922,6 +1922,9 @@ struct BusTransaction DBusList *connections; BusContext *context; DBusList *cancel_hooks; +#ifdef DBUS_ENABLE_CAPTURE + DBusList *captured_messages; +#endif }; static void @@ -2034,6 +2037,9 @@ bus_transaction_send_from_driver (BusTransaction *transaction, NULL, connection, connection, message, NULL)) return TRUE; + if (!bus_transaction_capture (transaction, message)) + return FALSE; + return bus_transaction_send (transaction, connection, message); } @@ -2125,15 +2131,51 @@ bus_transaction_send (BusTransaction *transaction, return TRUE; } +#ifdef DBUS_ENABLE_CAPTURE +/* + * Reserve enough memory to capture the given message if the + * transaction goes through. + */ +dbus_bool_t +bus_transaction_capture (BusTransaction *transaction, + DBusMessage *message) +{ + dbus_message_ref (message); + + if (!_dbus_list_append (&transaction->captured_messages, message)) + { + dbus_message_unref (message); + return FALSE; + } + + return TRUE; +} + static void -transaction_free (BusTransaction *transaction) +free_message_list (DBusList **list) { DBusMessage *message; + for (message = _dbus_list_pop_first (list); + message != NULL; + message = _dbus_list_pop_first (list)) + { + dbus_message_unref (message); + } +} +#endif + +static void +transaction_free (BusTransaction *transaction) +{ _dbus_assert (transaction->connections == NULL); free_cancel_hooks (transaction); +#ifdef DBUS_ENABLE_CAPTURE + free_message_list (&transaction->captured_messages); +#endif + dbus_free (transaction); } @@ -2191,6 +2233,11 @@ connection_execute_transaction (DBusConnection *connection, d = BUS_CONNECTION_DATA (connection); _dbus_assert (d != NULL); +#ifdef DBUS_ENABLE_CAPTURE + bus_context_capture_each (transaction->context, + &transaction->captured_messages); +#endif + /* Send the queue in order (FIFO) */ link = _dbus_list_get_last_link (&d->transaction_messages); while (link != NULL) diff --git a/bus/connection.h b/bus/connection.h index c936021..63d7a5c 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -124,6 +124,8 @@ BusConnections* bus_transaction_get_connections (BusTransaction * dbus_bool_t bus_transaction_send (BusTransaction *transaction, DBusConnection *connection, DBusMessage *message); +dbus_bool_t bus_transaction_capture (BusTransaction *transaction, + DBusMessage *message); dbus_bool_t bus_transaction_send_from_driver (BusTransaction *transaction, DBusConnection *connection, DBusMessage *message); @@ -151,4 +153,8 @@ int bus_connections_get_peak_bus_names_per_conn (BusConnections *connections); int bus_connection_get_peak_match_rules (DBusConnection *connection); int bus_connection_get_peak_bus_names (DBusConnection *connection); +#ifndef DBUS_ENABLE_CAPTURE +#define bus_transaction_capture(s, m) (TRUE) +#endif + #endif /* BUS_CONNECTION_H */ diff --git a/bus/dispatch.c b/bus/dispatch.c index 7a96f9d..0b0a343 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -103,6 +103,12 @@ bus_dispatch_matches (BusTransaction *transaction, context = bus_transaction_get_context (transaction); + if (!bus_transaction_capture (transaction, message)) + { + BUS_SET_OOM (error); + return FALSE; + } + /* First, send the message to the addressed_recipient, if there is one. */ if (addressed_recipient != NULL) { diff --git a/bus/session.conf.in b/bus/session.conf.in index 716b5e7..a2a34ab 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -14,6 +14,11 @@ @DBUS_SESSION_BUS_LISTEN_ADDRESS@ + + + diff --git a/bus/system.conf.in b/bus/system.conf.in index 92f4cc4..a29b137 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -26,6 +26,11 @@ @DBUS_LIBEXECDIR@/dbus-daemon-launch-helper + + + @DBUS_SYSTEM_PID_FILE@ diff --git a/configure.ac b/configure.ac index 9b578ed..0712739 100644 --- a/configure.ac +++ b/configure.ac @@ -1791,6 +1791,15 @@ if test "x$enable_stats" = xyes; then [Define to enable bus daemon usage statistics]) fi +AC_ARG_ENABLE([capture], + [AS_HELP_STRING([--enable-capture], + [enable message stream capture in pcap format])], + [], [enable_capture="$enable_developer"]) +if test "x$enable_capture" = xyes; then + AC_DEFINE([DBUS_ENABLE_CAPTURE], [1], + [Define to enable the pcap_recipient configuration option]) +fi + AC_CONFIG_FILES([ Doxyfile dbus/versioninfo.rc diff --git a/test/name-test/tmp-session-like-system.conf b/test/name-test/tmp-session-like-system.conf index 29ab115..52e8f9a 100644 --- a/test/name-test/tmp-session-like-system.conf +++ b/test/name-test/tmp-session-like-system.conf @@ -16,6 +16,8 @@ unix:tmpdir=/tmp + + -- 1.7.10.4