aboutsummaryrefslogtreecommitdiffstats
path: root/wiretap/systemd_journal.c
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2018-08-27 08:17:32 -0700
committerAnders Broman <a.broman58@gmail.com>2018-08-31 05:20:27 +0000
commit50b9da7bc2d43f83a57fdd1ff3a8c9186461d834 (patch)
tree0c6f820b6acef2f25ed4630d75f8a0995d57c6c8 /wiretap/systemd_journal.c
parent94735eb2bc02f7d683e50cfda1e5d0735cf5f97f (diff)
Add a systemd Journal Export Format parser and dissector.
Add a file parser and dissector that can handle the output of `journalctl -o export`. From here we can add a systemd journal extcap and possibly support for the JSON and binary formats. Change-Id: I01576959b2c347ce7ac9aa57cdb5c119c81d61e9 Reviewed-on: https://code.wireshark.org/review/29311 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'wiretap/systemd_journal.c')
-rw-r--r--wiretap/systemd_journal.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/wiretap/systemd_journal.c b/wiretap/systemd_journal.c
new file mode 100644
index 0000000000..fbff4656d9
--- /dev/null
+++ b/wiretap/systemd_journal.c
@@ -0,0 +1,236 @@
+/* systemd_journal.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "systemd_journal.h"
+
+// To do:
+// - Request a pcap encapsulation type.
+// - Should we add separate types for binary, plain, and JSON or add a metadata header?
+
+// Systemd journals are stored in the following formats:
+// Journal File Format (native binary): https://www.freedesktop.org/wiki/Software/systemd/journal-files/
+// Journal Export Format: https://www.freedesktop.org/wiki/Software/systemd/export/
+// Journal JSON format: https://www.freedesktop.org/wiki/Software/systemd/json/
+// This reads Journal Export Format files but could be extended to support
+// the binary and JSON formats.
+
+// Example data:
+// __CURSOR=s=1d56bab64d414960b9907ab0cc7f7c62;i=2;b=1497926e8b4b4d3ca6a5805e157fa73c;m=5d0ae5;t=56f2f5b66ce6f;x=20cb01e28bb496a8
+// __REALTIME_TIMESTAMP=1529624071163503
+// __MONOTONIC_TIMESTAMP=6097637
+// _BOOT_ID=1497926e8b4b4d3ca6a5805e157fa73c
+// PRIORITY=6
+// _MACHINE_ID=62c342838a6e436dacea041aa4b5064b
+// _HOSTNAME=example.wireshark.org
+// _SOURCE_MONOTONIC_TIMESTAMP=0
+// _TRANSPORT=kernel
+// SYSLOG_FACILITY=0
+// SYSLOG_IDENTIFIER=kernel
+// MESSAGE=Initializing cgroup subsys cpuset
+
+static gboolean systemd_journal_read(wtap *wth, int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean systemd_journal_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+// The Journal Export Format specification doesn't place limits on entry
+// lengths or lines per entry. We do.
+#define MAX_EXPORT_ENTRY_LENGTH WTAP_MAX_PACKET_SIZE_STANDARD
+#define MAX_EXPORT_ENTRY_LINES 100
+
+#define FLD__CURSOR "__CURSOR="
+#define FLD__REALTIME_TIMESTAMP "__REALTIME_TIMESTAMP="
+#define FLD__MONOTONIC_TIMESTAMP "__MONOTONIC_TIMESTAMP="
+
+wtap_open_return_val systemd_journal_open(wtap *wth, int *err _U_, gchar **err_info _U_)
+{
+ gchar *entry_buff = (gchar*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
+ gchar *entry_line = NULL;
+ gboolean got_cursor = FALSE;
+ gboolean got_rt_ts = FALSE;
+ gboolean got_mt_ts = FALSE;
+ int line_num;
+
+ errno = 0;
+ for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
+ entry_line = file_gets(entry_buff, MAX_EXPORT_ENTRY_LENGTH, wth->fh);
+ if (!entry_line) {
+ break;
+ }
+ if (entry_line[0] == '\n') {
+ break;
+ } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
+ got_cursor = TRUE;
+ } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, strlen(FLD__REALTIME_TIMESTAMP)) == 0) {
+ got_rt_ts = TRUE;
+ } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
+ got_mt_ts = TRUE;
+ }
+ }
+ g_free(entry_buff);
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (!got_cursor || !got_rt_ts || !got_mt_ts) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->subtype_read = systemd_journal_read;
+ wth->subtype_seek_read = systemd_journal_seek_read;
+ wth->file_encap = WTAP_ENCAP_SYSTEMD_JOURNAL;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean systemd_journal_read(wtap *wth, int *err _U_, gchar **err_info,
+ gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* Read record. */
+ if (!systemd_journal_read_export_entry(wth->fh, &wth->rec, wth->rec_data, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+ static gboolean
+systemd_journal_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read record. */
+ if (!systemd_journal_read_export_entry(wth->random_fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ if (*err == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ size_t fld_end = 0;
+ gchar *entry_buff = (gchar*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
+ gchar *entry_line = NULL;
+ gboolean got_cursor = FALSE;
+ gboolean got_rt_ts = FALSE;
+ gboolean got_mt_ts = FALSE;
+ gboolean got_double_newline = FALSE;
+ int line_num;
+ size_t rt_ts_len = strlen(FLD__REALTIME_TIMESTAMP);
+
+ for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
+ entry_line = file_gets(entry_buff + fld_end, MAX_EXPORT_ENTRY_LENGTH - (int) fld_end, fh);
+ if (!entry_line) {
+ break;
+ }
+ fld_end += strlen(entry_line);
+ if (entry_line[0] == '\n') {
+ got_double_newline = TRUE;
+ break;
+ } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
+ got_cursor = TRUE;
+ } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, rt_ts_len) == 0) {
+ errno = 0;
+ unsigned long rt_ts = strtoul(entry_line+rt_ts_len, NULL, 10);
+ if (!errno) {
+ rec->ts.secs = (time_t) rt_ts / 1000000;
+ rec->ts.nsecs = (rt_ts % 1000000) * 1000;
+ rec->tsprec = WTAP_TSPREC_USEC;
+ got_rt_ts = TRUE;
+ }
+ } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
+ got_mt_ts = TRUE;
+ } else if (!strstr(entry_line, "=")) {
+ // Start of binary data.
+ if (fld_end >= MAX_EXPORT_ENTRY_LENGTH - 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup_printf("systemd: binary length too long");
+ return FALSE;
+ }
+ guint64 data_len, le_data_len;
+ if (!wtap_read_bytes(fh, &le_data_len, 8, err, err_info)) {
+ return FALSE;
+ }
+ memcpy(entry_buff + fld_end, &le_data_len, 8);
+ fld_end += 8;
+ data_len = pletoh64(&le_data_len);
+ if (data_len < 1 || data_len - 1 >= MAX_EXPORT_ENTRY_LENGTH - fld_end) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup_printf("systemd: binary data too long");
+ return FALSE;
+ }
+ // Data + trailing \n
+ if (!wtap_read_bytes(fh, entry_buff + fld_end, (unsigned) data_len + 1, err, err_info)) {
+ return FALSE;
+ }
+ fld_end += data_len + 1;
+ }
+ if (MAX_EXPORT_ENTRY_LENGTH < fld_end + 2) { // \n\0
+ break;
+ }
+ }
+
+ if (!got_cursor || !got_rt_ts || !got_mt_ts) {
+ g_free(entry_buff);
+ return FALSE;
+ }
+
+ if (!got_double_newline && !file_eof(fh)) {
+ g_free(entry_buff);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.caplen = (guint32) fld_end;
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen + 1);
+ guint8 *pd = ws_buffer_start_ptr(buf);
+ entry_buff[fld_end+1] = '\0';
+ memcpy(pd, entry_buff, rec->rec_header.packet_header.caplen + 1);
+ g_free(entry_buff);
+
+ return TRUE;
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */