Skip to content

Commit 8d568e8

Browse files
authored
Merge pull request systemd#9346 from keszybz/journald-exact2
Store a copy of the input message if any stripping or truncation occurs
2 parents 2e7e8e3 + 7c30c3c commit 8d568e8

File tree

3 files changed

+101
-42
lines changed

3 files changed

+101
-42
lines changed

man/systemd.journal-fields.xml

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,34 @@
103103
<term><varname>SYSLOG_FACILITY=</varname></term>
104104
<term><varname>SYSLOG_IDENTIFIER=</varname></term>
105105
<term><varname>SYSLOG_PID=</varname></term>
106+
<term><varname>SYSLOG_TIMESTAMP=</varname></term>
106107
<listitem>
107-
<para>Syslog compatibility fields containing the facility
108-
(formatted as decimal string), the identifier string (i.e.
109-
"tag"), and the client PID. (Note that the tag is usually
110-
derived from glibc's
111-
<varname>program_invocation_short_name</varname> variable,
112-
see
108+
<para>Syslog compatibility fields containing the facility (formatted as
109+
decimal string), the identifier string (i.e. "tag"), the client PID, and
110+
the timestamp as specified in the original datagram. (Note that the tag is
111+
usually derived from glibc's
112+
<varname>program_invocation_short_name</varname> variable, see
113113
<citerefentry project='die-net'><refentrytitle>program_invocation_short_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>.)</para>
114114
</listitem>
115+
</varlistentry>
115116

117+
<varlistentry>
118+
<term><varname>SYSLOG_RAW=</varname></term>
119+
<listitem>
120+
<para>The original contents of the syslog line as received in the syslog
121+
datagram. This field is only included if the <varname>MESSAGE=</varname>
122+
field was modified compared to the original payload or the timestamp could
123+
not be located properly and is not included in
124+
<varname>SYSLOG_TIMESTAMP=</varname>. Message truncation occurs when when
125+
the message contains leading or trailing whitespace (trailing and leading
126+
whitespace is stripped), or it contains an embedded
127+
<constant>NUL</constant> byte (the <constant>NUL</constant> byte and
128+
anything after it is not included). Thus, the original syslog line is
129+
either stored as <varname>SYSLOG_RAW=</varname> or it can be recreated
130+
based on the stored priority and facility, timestamp, identifier, and the
131+
message payload in <varname>MESSAGE=</varname>.
132+
</para>
133+
</listitem>
116134
</varlistentry>
117135
</variablelist>
118136
</refsect1>

src/journal/journald-syslog.c

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
224224
return e;
225225
}
226226

227-
static void syslog_skip_date(char **buf) {
227+
static int syslog_skip_timestamp(const char **buf) {
228228
enum {
229229
LETTER,
230230
SPACE,
@@ -244,24 +244,21 @@ static void syslog_skip_date(char **buf) {
244244
SPACE
245245
};
246246

247-
char *p;
247+
const char *p, *t;
248248
unsigned i;
249249

250250
assert(buf);
251251
assert(*buf);
252252

253-
p = *buf;
254-
255-
for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
256-
253+
for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
257254
if (!*p)
258-
return;
255+
return 0;
259256

260257
switch (sequence[i]) {
261258

262259
case SPACE:
263260
if (*p != ' ')
264-
return;
261+
return 0;
265262
break;
266263

267264
case SPACE_OR_NUMBER:
@@ -271,75 +268,99 @@ static void syslog_skip_date(char **buf) {
271268
_fallthrough_;
272269
case NUMBER:
273270
if (*p < '0' || *p > '9')
274-
return;
271+
return 0;
275272

276273
break;
277274

278275
case LETTER:
279276
if (!(*p >= 'A' && *p <= 'Z') &&
280277
!(*p >= 'a' && *p <= 'z'))
281-
return;
278+
return 0;
282279

283280
break;
284281

285282
case COLON:
286283
if (*p != ':')
287-
return;
284+
return 0;
288285
break;
289286

290287
}
291288
}
292289

290+
t = *buf;
293291
*buf = p;
292+
return p - t;
294293
}
295294

296295
void server_process_syslog_message(
297296
Server *s,
298297
const char *buf,
299-
size_t buf_len,
298+
size_t raw_len,
300299
const struct ucred *ucred,
301300
const struct timeval *tv,
302301
const char *label,
303302
size_t label_len) {
304303

305-
char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
306-
syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)], *msg;
307-
const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
304+
char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
305+
syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
306+
const char *msg, *syslog_ts, *a;
308307
_cleanup_free_ char *identifier = NULL, *pid = NULL;
309308
int priority = LOG_USER | LOG_INFO, r;
310309
ClientContext *context = NULL;
311310
struct iovec *iovec;
312-
size_t n = 0, m, i;
311+
size_t n = 0, m, i, leading_ws, syslog_ts_len;
312+
bool store_raw;
313313

314314
assert(s);
315315
assert(buf);
316+
/* The message cannot be empty. */
317+
assert(raw_len > 0);
318+
/* The buffer NUL-terminated and can be used a string. raw_len is the length
319+
* without the terminating NUL byte, the buffer is actually one bigger. */
320+
assert(buf[raw_len] == '\0');
316321

317322
if (ucred && pid_is_valid(ucred->pid)) {
318323
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
319324
if (r < 0)
320325
log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
321326
}
322327

323-
/* We are creating copy of the message because we want to forward original message verbatim to the legacy
324-
syslog implementation */
325-
for (i = buf_len; i > 0; i--)
328+
/* We are creating a copy of the message because we want to forward the original message
329+
verbatim to the legacy syslog implementation */
330+
for (i = raw_len; i > 0; i--)
326331
if (!strchr(WHITESPACE, buf[i-1]))
327332
break;
328333

329-
msg = newa(char, i + 1);
330-
*((char *) mempcpy(msg, buf, i)) = 0;
331-
msg = skip_leading_chars(msg, WHITESPACE);
334+
leading_ws = strspn(buf, WHITESPACE);
332335

333-
syslog_parse_priority((const char **)&msg, &priority, true);
336+
if (i == raw_len)
337+
/* Nice! No need to strip anything on the end, let's optimize this a bit */
338+
msg = buf + leading_ws;
339+
else {
340+
msg = t = newa(char, i - leading_ws + 1);
341+
memcpy(t, buf + leading_ws, i - leading_ws);
342+
t[i - leading_ws] = 0;
343+
}
344+
345+
/* We will add the SYSLOG_RAW= field when we stripped anything
346+
* _or_ if the input message contained NUL bytes. */
347+
store_raw = msg != buf || strlen(msg) != raw_len;
348+
349+
syslog_parse_priority(&msg, &priority, true);
334350

335351
if (!client_context_test_priority(context, priority))
336352
return;
337353

338-
if (s->forward_to_syslog)
339-
forward_syslog_raw(s, priority, buf, buf_len, ucred, tv);
354+
syslog_ts = msg;
355+
syslog_ts_len = syslog_skip_timestamp(&msg);
356+
if (syslog_ts_len == 0)
357+
/* We failed to parse the full timestamp, store the raw message too */
358+
store_raw = true;
359+
360+
syslog_parse_identifier(&msg, &identifier, &pid);
340361

341-
syslog_skip_date(&msg);
342-
syslog_parse_identifier((const char**)&msg, &identifier, &pid);
362+
if (s->forward_to_syslog)
363+
forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
343364

344365
if (s->forward_to_kmsg)
345366
server_forward_kmsg(s, priority, identifier, msg, ucred);
@@ -350,7 +371,7 @@ void server_process_syslog_message(
350371
if (s->forward_to_wall)
351372
server_forward_wall(s, priority, identifier, msg, ucred);
352373

353-
m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context);
374+
m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
354375
iovec = newa(struct iovec, m);
355376

356377
iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
@@ -364,18 +385,37 @@ void server_process_syslog_message(
364385
}
365386

366387
if (identifier) {
367-
syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
368-
iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
388+
a = strjoina("SYSLOG_IDENTIFIER=", identifier);
389+
iovec[n++] = IOVEC_MAKE_STRING(a);
369390
}
370391

371392
if (pid) {
372-
syslog_pid = strjoina("SYSLOG_PID=", pid);
373-
iovec[n++] = IOVEC_MAKE_STRING(syslog_pid);
393+
a = strjoina("SYSLOG_PID=", pid);
394+
iovec[n++] = IOVEC_MAKE_STRING(a);
395+
}
396+
397+
if (syslog_ts_len > 0) {
398+
const size_t hlen = strlen("SYSLOG_TIMESTAMP=");
399+
400+
t = newa(char, hlen + raw_len);
401+
memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
402+
memcpy(t + hlen, syslog_ts, syslog_ts_len);
403+
404+
iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
374405
}
375406

376-
message = strjoina("MESSAGE=", msg);
377-
if (message)
378-
iovec[n++] = IOVEC_MAKE_STRING(message);
407+
a = strjoina("MESSAGE=", msg);
408+
iovec[n++] = IOVEC_MAKE_STRING(a);
409+
410+
if (store_raw) {
411+
const size_t hlen = strlen("SYSLOG_RAW=");
412+
413+
t = newa(char, hlen + raw_len);
414+
memcpy(t, "SYSLOG_RAW=", hlen);
415+
memcpy(t + hlen, buf, raw_len);
416+
417+
iovec[n++] = IOVEC_MAKE(t, hlen + raw_len);
418+
}
379419

380420
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
381421
}

src/systemctl/systemctl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7838,7 +7838,8 @@ static int parse_shutdown_time_spec(const char *t, usec_t *_u) {
78387838
tm.tm_min = (int) minute;
78397839
tm.tm_sec = 0;
78407840

7841-
assert_se(s = mktime(&tm));
7841+
s = mktime(&tm);
7842+
assert(s >= 0);
78427843

78437844
*_u = (usec_t) s * USEC_PER_SEC;
78447845

0 commit comments

Comments
 (0)