20800366dd
Change all wireshark.org URLs to use https. Fix some broken links while we're at it. Change-Id: I161bf8eeca43b8027605acea666032da86f5ea1c Reviewed-on: https://code.wireshark.org/review/34089 Reviewed-by: Guy Harris <guy@alum.mit.edu>
234 lines
7.9 KiB
C
234 lines
7.9 KiB
C
/* 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 "pcapng_module.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, wtap_rec *rec, Buffer *buf,
|
|
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->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL;
|
|
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, wtap_rec *rec, Buffer *buf,
|
|
int *err, gchar **err_info, gint64 *data_offset)
|
|
{
|
|
*data_offset = file_tell(wth->fh);
|
|
|
|
/* Read record. */
|
|
if (!systemd_journal_read_export_entry(wth->fh, rec, buf, 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 *buf_ptr;
|
|
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);
|
|
|
|
ws_buffer_assure_space(buf, MAX_EXPORT_ENTRY_LENGTH);
|
|
buf_ptr = (gchar *) ws_buffer_start_ptr(buf);
|
|
|
|
for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
|
|
entry_line = file_gets(buf_ptr + 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(buf_ptr + 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, buf_ptr + fld_end, (unsigned) data_len + 1, err, err_info)) {
|
|
return FALSE;
|
|
}
|
|
fld_end += (size_t) data_len + 1;
|
|
}
|
|
if (MAX_EXPORT_ENTRY_LENGTH < fld_end + 2) { // \n\0
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!got_cursor || !got_rt_ts || !got_mt_ts) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!got_double_newline && !file_eof(fh)) {
|
|
return FALSE;
|
|
}
|
|
|
|
rec->rec_type = REC_TYPE_FT_SPECIFIC_EVENT;
|
|
rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
|
|
rec->rec_header.ft_specific_header.record_type = WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL;
|
|
rec->rec_header.ft_specific_header.record_len = (guint32) fld_end;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Editor modelines - https://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:
|
|
*/
|