chiark / gitweb /
busctl: add new "capture" verb to record bus messages in libpcap compatible files...
authorLennart Poettering <lennart@poettering.net>
Thu, 30 Oct 2014 00:13:11 +0000 (01:13 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 30 Oct 2014 00:13:54 +0000 (01:13 +0100)
man/busctl.xml
src/libsystemd/sd-bus/bus-dump.c
src/libsystemd/sd-bus/bus-dump.h
src/libsystemd/sd-bus/busctl.c

index 9865b17..b358bc4 100644 (file)
@@ -133,6 +133,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--size=</option></term>
+
+        <listitem>
+          <para>When used with the <command>capture</command> command
+          specifies the maximum bus message size to capture
+          ("snaplen"). Defaults to 4096 bytes.</para>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="user" />
       <xi:include href="user-system-options.xml" xpointer="system" />
       <xi:include href="user-system-options.xml" xpointer="host" />
@@ -167,6 +177,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       </varlistentry>
 
       <varlistentry>
+        <term><command>capture</command>  <arg choice="opt" rep="repeat"><replaceable>NAME</replaceable></arg></term>
+
+        <listitem><para>Similar to <command>monitor</command> but
+        writes the output in pcap format (for details see the <ulink
+        url="http://wiki.wireshark.org/Development/LibpcapFileFormat">Libpcap
+        File Format</ulink> description. Make sure to redirect the
+        output to STDOUT to a file. Tools like
+        <citerefentry><refentrytitle>wireshark</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        may be used to dissect and view the generated
+        files.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><command>status</command>  <arg choice="plain"><replaceable>NAME</replaceable></arg></term>
 
         <listitem><para>Show process information and credentials of a
@@ -191,7 +214,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-bus-proxyd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>wireshark</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
 </refentry>
index 8b70b20..b536641 100644 (file)
@@ -23,6 +23,7 @@
 #include "capability.h"
 #include "strv.h"
 #include "audit.h"
+#include "macro.h"
 
 #include "bus-message.h"
 #include "bus-internal.h"
@@ -424,3 +425,98 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f) {
 
         return 0;
 }
+
+/*
+ * For details about the file format, see:
+ *
+ * http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+
+typedef struct _packed_ pcap_hdr_s {
+        uint32_t magic_number;   /* magic number */
+        uint16_t version_major;  /* major version number */
+        uint16_t version_minor;  /* minor version number */
+        int32_t  thiszone;       /* GMT to local correction */
+        uint32_t sigfigs;        /* accuracy of timestamps */
+        uint32_t snaplen;        /* max length of captured packets, in octets */
+        uint32_t network;        /* data link type */
+} pcap_hdr_t ;
+
+typedef struct  _packed_ pcaprec_hdr_s {
+        uint32_t ts_sec;         /* timestamp seconds */
+        uint32_t ts_usec;        /* timestamp microseconds */
+        uint32_t incl_len;       /* number of octets of packet saved in file */
+        uint32_t orig_len;       /* actual length of packet */
+} pcaprec_hdr_t;
+
+int bus_pcap_header(size_t snaplen, FILE *f) {
+
+        pcap_hdr_t hdr = {
+                .magic_number = 0xa1b2c3d4U,
+                .version_major = 2,
+                .version_minor = 4,
+                .thiszone = 0, /* UTC */
+                .sigfigs = 0,
+                .network = 231, /* D-Bus */
+        };
+
+        if (!f)
+                f = stdout;
+
+        assert(snaplen > 0);
+        assert((size_t) (uint32_t) snaplen == snaplen);
+
+        hdr.snaplen = (uint32_t) snaplen;
+
+        fwrite(&hdr, 1, sizeof(hdr), f);
+        fflush(f);
+
+        return 0;
+}
+
+int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
+        struct bus_body_part *part;
+        pcaprec_hdr_t hdr = {};
+        struct timeval tv;
+        unsigned i;
+        size_t w;
+
+        if (!f)
+                f = stdout;
+
+        assert(m);
+        assert(snaplen > 0);
+        assert((size_t) (uint32_t) snaplen == snaplen);
+
+        if (m->realtime != 0)
+                timeval_store(&tv, m->realtime);
+        else
+                assert_se(gettimeofday(&tv, NULL) >= 0);
+
+        hdr.ts_sec = tv.tv_sec;
+        hdr.ts_usec = tv.tv_usec;
+        hdr.orig_len = BUS_MESSAGE_SIZE(m);
+        hdr.incl_len = MIN(hdr.orig_len, snaplen);
+
+        /* write the pcap header */
+        fwrite(&hdr, 1, sizeof(hdr), f);
+
+        /* write the dbus header */
+        w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen);
+        fwrite(m->header, 1, w, f);
+        snaplen -= w;
+
+        /* write the dbus body */
+        MESSAGE_FOREACH_PART(part, i, m) {
+                if (snaplen <= 0)
+                        break;
+
+                w = MIN(part->size, snaplen);
+                fwrite(part->data, 1, w, f);
+                snaplen -= w;
+        }
+
+        fflush(f);
+
+        return 0;
+}
index bb1d25d..6af0ab0 100644 (file)
@@ -29,3 +29,6 @@
 int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header);
 
 int bus_creds_dump(sd_bus_creds *c, FILE *f);
+
+int bus_pcap_header(size_t snaplen, FILE *f);
+int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f);
index fdc275c..faa0c60 100644 (file)
@@ -44,6 +44,7 @@ static char **arg_matches = NULL;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static char *arg_host = NULL;
 static bool arg_user = false;
+static size_t arg_snaplen = 4096;
 
 static void pager_open_if_enabled(void) {
 
@@ -223,7 +224,15 @@ static int list_bus_names(sd_bus *bus, char **argv) {
         return 0;
 }
 
-static int monitor(sd_bus *bus, char *argv[]) {
+static int message_dump(sd_bus_message *m, FILE *f) {
+        return bus_message_dump(m, f, true);
+}
+
+static int message_pcap(sd_bus_message *m, FILE *f) {
+        return bus_message_pcap_frame(m, arg_snaplen, f);
+}
+
+static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
         bool added_something = false;
         char **i;
         int r;
@@ -277,7 +286,7 @@ static int monitor(sd_bus *bus, char *argv[]) {
                 }
 
                 if (m) {
-                        bus_message_dump(m, stdout, true);
+                        dump(m, stdout);
                         continue;
                 }
 
@@ -292,6 +301,28 @@ static int monitor(sd_bus *bus, char *argv[]) {
         }
 }
 
+static int capture(sd_bus *bus, char *argv[]) {
+        int r;
+
+        if (isatty(fileno(stdout)) > 0) {
+                log_error("Refusing to write message data to console, please redirect output to a file.");
+                return -EINVAL;
+        }
+
+        bus_pcap_header(arg_snaplen, stdout);
+
+        r = monitor(bus, argv, message_pcap);
+        if (r < 0)
+                return r;
+
+        if (ferror(stdout)) {
+                log_error("Couldn't write capture file.");
+                return -EIO;
+        }
+
+        return r;
+}
+
 static int status(sd_bus *bus, char *argv[]) {
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         pid_t pid;
@@ -339,6 +370,7 @@ static int help(void) {
                "Commands:\n"
                "  list                    List bus names\n"
                "  monitor [SERVICE...]    Show bus traffic\n"
+               "  capture [SERVICE...]    Capture bus traffic as pcap\n"
                "  status NAME             Show name status\n"
                "  help                    Show this help\n"
                , program_invocation_short_name);
@@ -359,7 +391,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SHOW_MACHINE,
                 ARG_UNIQUE,
                 ARG_ACQUIRED,
-                ARG_ACTIVATABLE
+                ARG_ACTIVATABLE,
+                ARG_SIZE,
         };
 
         static const struct option options[] = {
@@ -377,10 +410,11 @@ static int parse_argv(int argc, char *argv[]) {
                 { "match",        required_argument, NULL, ARG_MATCH        },
                 { "host",         required_argument, NULL, 'H'              },
                 { "machine",      required_argument, NULL, 'M'              },
+                { "size",         required_argument, NULL, ARG_SIZE         },
                 {},
         };
 
-        int c;
+        int c, r;
 
         assert(argc >= 0);
         assert(argv);
@@ -438,6 +472,24 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_oom();
                         break;
 
+                case ARG_SIZE: {
+                        off_t o;
+
+                        r = parse_size(optarg, 0, &o);
+                        if (r < 0) {
+                                log_error("Failed to parse size: %s", optarg);
+                                return r;
+                        }
+
+                        if ((off_t) (size_t) o !=  o) {
+                                log_error("Size out of range.");
+                                return -E2BIG;
+                        }
+
+                        arg_snaplen = (size_t) o;
+                        break;
+                }
+
                 case 'H':
                         arg_transport = BUS_TRANSPORT_REMOTE;
                         arg_host = optarg;
@@ -469,7 +521,10 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
                 return list_bus_names(bus, argv + optind);
 
         if (streq(argv[optind], "monitor"))
-                return monitor(bus, argv + optind);
+                return monitor(bus, argv + optind, message_dump);
+
+        if (streq(argv[optind], "capture"))
+                return capture(bus, argv + optind);
 
         if (streq(argv[optind], "status"))
                 return status(bus, argv + optind);
@@ -498,7 +553,8 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        if (streq_ptr(argv[optind], "monitor")) {
+        if (streq_ptr(argv[optind], "monitor") ||
+            streq_ptr(argv[optind], "capture")) {
 
                 r = sd_bus_set_monitor(bus, true);
                 if (r < 0) {