From 1f70b0876a9388f38422c12fa0c73761559d9425 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 30 Oct 2014 01:13:11 +0100 Subject: [PATCH] busctl: add new "capture" verb to record bus messages in libpcap compatible files, for dissection with wireshark --- man/busctl.xml | 26 ++++++++- src/libsystemd/sd-bus/bus-dump.c | 96 ++++++++++++++++++++++++++++++++ src/libsystemd/sd-bus/bus-dump.h | 3 + src/libsystemd/sd-bus/busctl.c | 68 ++++++++++++++++++++-- 4 files changed, 186 insertions(+), 7 deletions(-) diff --git a/man/busctl.xml b/man/busctl.xml index 9865b17d5..b358bc4cd 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -133,6 +133,16 @@ along with systemd; If not, see . + + + + + When used with the capture command + specifies the maximum bus message size to capture + ("snaplen"). Defaults to 4096 bytes. + + + @@ -166,6 +176,19 @@ along with systemd; If not, see . bus. + + capture NAME + + Similar to monitor but + writes the output in pcap format (for details see the Libpcap + File Format description. Make sure to redirect the + output to STDOUT to a file. Tools like + wireshark1 + may be used to dissect and view the generated + files. + + status NAME @@ -191,7 +214,8 @@ along with systemd; If not, see . sd-bus3, systemd1, systemd-bus-proxyd8, - machinectl1 + machinectl1, + wireshark1 diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 8b70b20e8..b536641eb 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -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; +} diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index bb1d25dc4..6af0ab066 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -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); diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index fdc275ce4..faa0c6091 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -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) { -- 2.30.2