mirror of
https://github.com/systemd/systemd.git
synced 2025-03-25 18:50:18 +03:00
busctl: add new "capture" verb to record bus messages in libpcap compatible files, for dissection with wireshark
This commit is contained in:
parent
1ab19cb167
commit
1f70b0876a
@ -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" />
|
||||
@ -166,6 +176,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
bus.</para></listitem>
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user