attr_dissector_fn_t
[wireshark-sm.git] / wiretap / systemd_journal.c
blob2a0fab9513a5e803da35abdcda2b1b75693cc1e3
1 /* systemd_journal.c
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "config.h"
10 #include "systemd_journal.h"
12 #include <errno.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include "wtap-int.h"
16 #include "pcapng_module.h"
17 #include "file_wrappers.h"
19 // To do:
20 // - Request a pcap encapsulation type.
21 // - Should we add separate types for binary, plain, and JSON or add a metadata header?
23 // Systemd journals are stored in the following formats:
24 // Journal File Format (native binary): https://www.freedesktop.org/wiki/Software/systemd/journal-files/
25 // Journal Export Format: https://www.freedesktop.org/wiki/Software/systemd/export/
26 // Journal JSON format: https://www.freedesktop.org/wiki/Software/systemd/json/
27 // This reads Journal Export Format files but could be extended to support
28 // the binary and JSON formats.
30 // Example data:
31 // __CURSOR=s=1d56bab64d414960b9907ab0cc7f7c62;i=2;b=1497926e8b4b4d3ca6a5805e157fa73c;m=5d0ae5;t=56f2f5b66ce6f;x=20cb01e28bb496a8
32 // __REALTIME_TIMESTAMP=1529624071163503
33 // __MONOTONIC_TIMESTAMP=6097637
34 // _BOOT_ID=1497926e8b4b4d3ca6a5805e157fa73c
35 // PRIORITY=6
36 // _MACHINE_ID=62c342838a6e436dacea041aa4b5064b
37 // _HOSTNAME=example.wireshark.org
38 // _SOURCE_MONOTONIC_TIMESTAMP=0
39 // _TRANSPORT=kernel
40 // SYSLOG_FACILITY=0
41 // SYSLOG_IDENTIFIER=kernel
42 // MESSAGE=Initializing cgroup subsys cpuset
44 static bool systemd_journal_read(wtap *wth, wtap_rec *rec, Buffer *buf,
45 int *err, char **err_info, int64_t *data_offset);
46 static bool systemd_journal_seek_read(wtap *wth, int64_t seek_off,
47 wtap_rec *rec, Buffer *buf, int *err, char **err_info);
48 static bool systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec,
49 Buffer *buf, int *err, char **err_info);
51 // The Journal Export Format specification doesn't place limits on entry
52 // lengths or lines per entry. We do.
53 #define MAX_EXPORT_ENTRY_LENGTH WTAP_MAX_PACKET_SIZE_STANDARD
54 #define MAX_EXPORT_ENTRY_LINES 100
56 // Strictly speaking, we only need __REALTIME_TIMESTAMP= since we use
57 // that to set the packet timestamp. According to
58 // https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
59 // __CURSOR= and __MONOTONIC_TIMESTAMP= should be present as well, so
60 // check for them order to improve our heuristics.
61 #define FLD__CURSOR "__CURSOR="
62 #define FLD__REALTIME_TIMESTAMP "__REALTIME_TIMESTAMP="
63 #define FLD__MONOTONIC_TIMESTAMP "__MONOTONIC_TIMESTAMP="
65 static int systemd_journal_file_type_subtype = -1;
67 void register_systemd_journal(void);
69 wtap_open_return_val systemd_journal_open(wtap *wth, int *err _U_, char **err_info _U_)
71 char *entry_buff = (char*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
72 char *entry_line = NULL;
73 bool got_cursor = false;
74 bool got_rt_ts = false;
75 bool got_mt_ts = false;
76 int line_num;
78 errno = 0;
79 for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
80 entry_line = file_gets(entry_buff, MAX_EXPORT_ENTRY_LENGTH, wth->fh);
81 if (!entry_line) {
82 break;
84 if (entry_line[0] == '\n') {
85 break;
86 } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
87 got_cursor = true;
88 } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, strlen(FLD__REALTIME_TIMESTAMP)) == 0) {
89 got_rt_ts = true;
90 } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
91 got_mt_ts = true;
94 g_free(entry_buff);
96 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
97 return WTAP_OPEN_ERROR;
100 if (!got_cursor || !got_rt_ts || !got_mt_ts) {
101 return WTAP_OPEN_NOT_MINE;
104 wth->file_type_subtype = systemd_journal_file_type_subtype;
105 wth->subtype_read = systemd_journal_read;
106 wth->subtype_seek_read = systemd_journal_seek_read;
107 wth->file_encap = WTAP_ENCAP_SYSTEMD_JOURNAL;
108 wth->file_tsprec = WTAP_TSPREC_USEC;
111 * Add an IDB; we don't know how many interfaces were
112 * involved, so we just say one interface, about which
113 * we only know the link-layer type, snapshot length,
114 * and time stamp resolution.
116 wtap_add_generated_idb(wth);
118 return WTAP_OPEN_MINE;
121 /* Read the next packet */
122 static bool systemd_journal_read(wtap *wth, wtap_rec *rec, Buffer *buf,
123 int *err, char **err_info, int64_t *data_offset)
125 *data_offset = file_tell(wth->fh);
127 /* Read record. */
128 if (!systemd_journal_read_export_entry(wth->fh, rec, buf, err, err_info)) {
129 /* Read error or EOF */
130 return false;
133 return true;
136 static bool
137 systemd_journal_seek_read(wtap *wth, int64_t seek_off,
138 wtap_rec *rec, Buffer *buf,
139 int *err, char **err_info)
141 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
142 return false;
144 /* Read record. */
145 if (!systemd_journal_read_export_entry(wth->random_fh, rec, buf, err, err_info)) {
146 /* Read error or EOF */
147 if (*err == 0) {
148 /* EOF means "short read" in random-access mode */
149 *err = WTAP_ERR_SHORT_READ;
151 return false;
153 return true;
156 static bool
157 systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
159 size_t fld_end = 0;
160 char *buf_ptr;
161 char *entry_line = NULL;
162 bool got_cursor = false;
163 bool got_rt_ts = false;
164 bool got_mt_ts = false;
165 bool got_double_newline = false;
166 int line_num;
167 size_t rt_ts_len = strlen(FLD__REALTIME_TIMESTAMP);
169 ws_buffer_assure_space(buf, MAX_EXPORT_ENTRY_LENGTH);
170 buf_ptr = (char *) ws_buffer_start_ptr(buf);
172 for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
173 entry_line = file_gets(buf_ptr + fld_end, MAX_EXPORT_ENTRY_LENGTH - (int) fld_end, fh);
174 if (!entry_line) {
175 break;
177 fld_end += strlen(entry_line);
178 if (entry_line[0] == '\n') {
179 got_double_newline = true;
180 break;
181 } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
182 got_cursor = true;
183 } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, rt_ts_len) == 0) {
184 errno = 0;
185 unsigned long rt_ts = strtoul(entry_line+rt_ts_len, NULL, 10);
186 if (!errno) {
187 rec->ts.secs = (time_t) rt_ts / 1000000;
188 rec->ts.nsecs = (rt_ts % 1000000) * 1000;
189 rec->tsprec = WTAP_TSPREC_USEC;
190 got_rt_ts = true;
192 } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
193 got_mt_ts = true;
194 } else if (!strstr(entry_line, "=")) {
195 // Start of binary data.
196 if (fld_end >= MAX_EXPORT_ENTRY_LENGTH - 8) {
197 *err = WTAP_ERR_BAD_FILE;
198 *err_info = ws_strdup_printf("systemd: binary length too long");
199 return false;
201 uint64_t data_len, le_data_len;
202 if (!wtap_read_bytes(fh, &le_data_len, 8, err, err_info)) {
203 return false;
205 memcpy(buf_ptr + fld_end, &le_data_len, 8);
206 fld_end += 8;
207 data_len = pletoh64(&le_data_len);
208 if (data_len < 1 || data_len - 1 >= MAX_EXPORT_ENTRY_LENGTH - fld_end) {
209 *err = WTAP_ERR_BAD_FILE;
210 *err_info = ws_strdup_printf("systemd: binary data too long");
211 return false;
213 // Data + trailing \n
214 if (!wtap_read_bytes(fh, buf_ptr + fld_end, (unsigned) data_len + 1, err, err_info)) {
215 return false;
217 fld_end += (size_t) data_len + 1;
219 if (MAX_EXPORT_ENTRY_LENGTH < fld_end + 2) { // \n\0
220 break;
224 if (!got_cursor || !got_rt_ts || !got_mt_ts) {
225 return false;
228 if (!got_double_newline && !file_eof(fh)) {
229 return false;
232 rec->rec_type = REC_TYPE_SYSTEMD_JOURNAL_EXPORT;
233 rec->block = wtap_block_create(WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT);
234 rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
235 rec->rec_header.systemd_journal_export_header.record_len = (uint32_t) fld_end;
237 return true;
240 static const struct supported_block_type systemd_journal_blocks_supported[] = {
242 * We support systemd journal blocks, with no comments or other options.
244 { WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
247 static const struct file_type_subtype_info systemd_journal_info = {
248 "systemd journal export", "systemd_journal", NULL, NULL,
249 false, BLOCKS_SUPPORTED(systemd_journal_blocks_supported),
250 NULL, NULL, NULL
253 void register_systemd_journal(void)
255 systemd_journal_file_type_subtype = wtap_register_file_type_subtype(&systemd_journal_info);
258 * Register name for backwards compatibility with the
259 * wtap_filetypes table in Lua.
261 wtap_register_backwards_compatibility_lua_name("SYSTEMD_JOURNAL",
262 systemd_journal_file_type_subtype);
266 * Editor modelines - https://www.wireshark.org/tools/modelines.html
268 * Local variables:
269 * c-basic-offset: 4
270 * tab-width: 8
271 * indent-tabs-mode: nil
272 * End:
274 * vi: set shiftwidth=4 tabstop=8 expandtab:
275 * :indentSize=4:tabSize=8:noTabs=true: