]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
Add hasprefix macro to check prefixes of fixed length
[thirdparty/systemd.git] / src / shared / logs-show.c
CommitLineData
86aa7ba4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
86aa7ba4
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
86aa7ba4 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
86aa7ba4
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <assert.h>
24#include <errno.h>
df50185b 25#include <sys/poll.h>
55d7bfc1 26#include <string.h>
86aa7ba4
LP
27
28#include "logs-show.h"
29#include "log.h"
30#include "util.h"
ba961854 31#include "utf8.h"
d99ae53a 32#include "hashmap.h"
dfb33a97 33#include "journal-internal.h"
86aa7ba4
LP
34
35#define PRINT_THRESHOLD 128
08ace05b 36#define JSON_THRESHOLD 4096
86aa7ba4 37
d4205751
LP
38static int print_catalog(FILE *f, sd_journal *j) {
39 int r;
40 _cleanup_free_ char *t = NULL, *z = NULL;
41
42
43 r = sd_journal_get_catalog(j, &t);
44 if (r < 0)
45 return r;
46
47 z = strreplace(strstrip(t), "\n", "\n-- ");
48 if (!z)
49 return log_oom();
50
51 fputs("-- ", f);
52 fputs(z, f);
53 fputc('\n', f);
54
55 return 0;
56}
57
55d7bfc1
LP
58static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
59 size_t fl, nl;
60 void *buf;
61
62 assert(data);
63 assert(field);
64 assert(target);
65 assert(target_size);
66
67 fl = strlen(field);
68 if (length < fl)
69 return 0;
70
71 if (memcmp(data, field, fl))
72 return 0;
73
74 nl = length - fl;
bf967366 75 buf = malloc(nl+1);
0d0f0c50
SL
76 if (!buf)
77 return log_oom();
55d7bfc1 78
46b0d922
LP
79 memcpy(buf, (const char*) data + fl, nl);
80 ((char*)buf)[nl] = 0;
81
6c1e6b98 82 free(*target);
55d7bfc1
LP
83 *target = buf;
84 *target_size = nl;
85
86 return 1;
87}
88
08ace05b
LP
89static bool shall_print(const char *p, size_t l, OutputFlags flags) {
90 assert(p);
91
92 if (flags & OUTPUT_SHOW_ALL)
55d7bfc1
LP
93 return true;
94
93b73b06 95 if (l >= PRINT_THRESHOLD)
55d7bfc1
LP
96 return false;
97
31f7bf19 98 if (!utf8_is_printable(p, l))
55d7bfc1
LP
99 return false;
100
101 return true;
102}
103
8c1396b1 104static void print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputMode flags, int priority, const char* message, size_t message_len) {
31f7bf19
ZJS
105 const char *color_on = "", *color_off = "";
106 const char *pos, *end;
107 bool continuation = false;
108
109 if (flags & OUTPUT_COLOR) {
110 if (priority <= LOG_ERR) {
111 color_on = ANSI_HIGHLIGHT_RED_ON;
112 color_off = ANSI_HIGHLIGHT_OFF;
113 } else if (priority <= LOG_NOTICE) {
114 color_on = ANSI_HIGHLIGHT_ON;
115 color_off = ANSI_HIGHLIGHT_OFF;
116 }
117 }
118
119 for (pos = message; pos < message + message_len; pos = end + 1) {
120 int len;
121 for (end = pos; end < message + message_len && *end != '\n'; end++)
122 ;
123 len = end - pos;
124 assert(len >= 0);
125
8c1396b1 126 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || prefix + len + 1 < n_columns)
31f7bf19
ZJS
127 fprintf(f, "%*s%s%.*s%s\n",
128 continuation * prefix, "",
129 color_on, len, pos, color_off);
130 else if (prefix < n_columns && n_columns - prefix >= 3) {
131 _cleanup_free_ char *e;
132
133 e = ellipsize_mem(pos, len, n_columns - prefix, 90);
134
135 if (!e)
136 fprintf(f, "%s%.*s%s\n", color_on, len, pos, color_off);
137 else
138 fprintf(f, "%s%s%s\n", color_on, e, color_off);
139 } else
140 fputs("...\n", f);
141
142 continuation = true;
143 }
144}
145
08ace05b
LP
146static int output_short(
147 FILE *f,
148 sd_journal *j,
149 OutputMode mode,
150 unsigned n_columns,
151 OutputFlags flags) {
152
86aa7ba4 153 int r;
86aa7ba4
LP
154 const void *data;
155 size_t length;
156 size_t n = 0;
08ace05b 157 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
49826187
LP
158 size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0;
159 int p = LOG_INFO;
86aa7ba4 160
08ace05b 161 assert(f);
86aa7ba4
LP
162 assert(j);
163
93b73b06
LP
164 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
165
a72b6353 166 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
55d7bfc1 167
49826187
LP
168 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
169 if (r < 0)
08ace05b 170 return r;
49826187
LP
171 else if (r > 0)
172 continue;
173
55d7bfc1
LP
174 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
175 if (r < 0)
08ace05b 176 return r;
55d7bfc1
LP
177 else if (r > 0)
178 continue;
179
4cd9a9d9 180 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
55d7bfc1 181 if (r < 0)
08ace05b 182 return r;
55d7bfc1
LP
183 else if (r > 0)
184 continue;
185
186 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
187 if (r < 0)
08ace05b 188 return r;
55d7bfc1
LP
189 else if (r > 0)
190 continue;
191
192 r = parse_field(data, length, "_PID=", &pid, &pid_len);
193 if (r < 0)
08ace05b 194 return r;
55d7bfc1
LP
195 else if (r > 0)
196 continue;
197
6c1e6b98
LP
198 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
199 if (r < 0)
08ace05b 200 return r;
6c1e6b98
LP
201 else if (r > 0)
202 continue;
203
bf967366
LP
204 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
205 if (r < 0)
08ace05b 206 return r;
bf967366
LP
207 else if (r > 0)
208 continue;
209
210 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
211 if (r < 0)
08ace05b 212 return r;
bf967366
LP
213 else if (r > 0)
214 continue;
215
55d7bfc1
LP
216 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
217 if (r < 0)
08ace05b 218 return r;
55d7bfc1
LP
219 }
220
a72b6353
ZJS
221 if (r < 0)
222 return r;
223
08ace05b
LP
224 if (!message)
225 return 0;
55d7bfc1 226
e8bc0ea2
LP
227 if (!(flags & OUTPUT_SHOW_ALL))
228 strip_tab_ansi(&message, &message_len);
229
49826187
LP
230 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
231 p = *priority - '0';
232
a6e87e90 233 if (mode == OUTPUT_SHORT_MONOTONIC) {
67a12205 234 uint64_t t;
3ebcdf8c
LP
235 sd_id128_t boot_id;
236
bf967366
LP
237 r = -ENOENT;
238
239 if (monotonic)
240 r = safe_atou64(monotonic, &t);
241
242 if (r < 0)
3ebcdf8c 243 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
86aa7ba4 244
3ebcdf8c 245 if (r < 0) {
a72b6353 246 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
08ace05b 247 return r;
67a12205
LP
248 }
249
08ace05b
LP
250 fprintf(f, "[%5llu.%06llu]",
251 (unsigned long long) (t / USEC_PER_SEC),
252 (unsigned long long) (t % USEC_PER_SEC));
3ebcdf8c
LP
253
254 n += 1 + 5 + 1 + 6 + 1;
255
67a12205
LP
256 } else {
257 char buf[64];
bf967366 258 uint64_t x;
67a12205
LP
259 time_t t;
260 struct tm tm;
731a676c 261
bf967366
LP
262 r = -ENOENT;
263
264 if (realtime)
265 r = safe_atou64(realtime, &x);
266
267 if (r < 0)
268 r = sd_journal_get_realtime_usec(j, &x);
67a12205 269
67a12205 270 if (r < 0) {
a72b6353 271 log_error("Failed to get realtime timestamp: %s", strerror(-r));
08ace05b 272 return r;
67a12205
LP
273 }
274
bf967366 275 t = (time_t) (x / USEC_PER_SEC);
67a12205
LP
276 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
277 log_error("Failed to format time.");
08ace05b 278 return r;
67a12205
LP
279 }
280
08ace05b 281 fputs(buf, f);
67a12205
LP
282 n += strlen(buf);
283 }
86aa7ba4 284
08ace05b
LP
285 if (hostname && shall_print(hostname, hostname_len, flags)) {
286 fprintf(f, " %.*s", (int) hostname_len, hostname);
55d7bfc1
LP
287 n += hostname_len + 1;
288 }
289
08ace05b
LP
290 if (identifier && shall_print(identifier, identifier_len, flags)) {
291 fprintf(f, " %.*s", (int) identifier_len, identifier);
4cd9a9d9 292 n += identifier_len + 1;
08ace05b
LP
293 } else if (comm && shall_print(comm, comm_len, flags)) {
294 fprintf(f, " %.*s", (int) comm_len, comm);
55d7bfc1 295 n += comm_len + 1;
b5936820 296 } else
08ace05b 297 fputc(' ', f);
86aa7ba4 298
08ace05b
LP
299 if (pid && shall_print(pid, pid_len, flags)) {
300 fprintf(f, "[%.*s]", (int) pid_len, pid);
55d7bfc1 301 n += pid_len + 2;
08ace05b
LP
302 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
303 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
6c1e6b98 304 n += fake_pid_len + 2;
86aa7ba4
LP
305 }
306
31f7bf19 307 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
e6acda19 308 char bytes[FORMAT_BYTES_MAX];
08ace05b 309 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
31f7bf19
ZJS
310 } else {
311 fputs(": ", f);
312 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
313 }
55d7bfc1 314
d4205751
LP
315 if (flags & OUTPUT_CATALOG)
316 print_catalog(f, j);
317
08ace05b 318 return 0;
86aa7ba4
LP
319}
320
08ace05b
LP
321static int output_verbose(
322 FILE *f,
323 sd_journal *j,
324 OutputMode mode,
325 unsigned n_columns,
326 OutputFlags flags) {
327
86aa7ba4
LP
328 const void *data;
329 size_t length;
7fd1b19b 330 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
331 uint64_t realtime;
332 char ts[FORMAT_TIMESTAMP_MAX];
333 int r;
334
08ace05b 335 assert(f);
86aa7ba4
LP
336 assert(j);
337
93b73b06
LP
338 sd_journal_set_data_threshold(j, 0);
339
86aa7ba4
LP
340 r = sd_journal_get_realtime_usec(j, &realtime);
341 if (r < 0) {
a72b6353
ZJS
342 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
343 "Failed to get realtime timestamp: %s", strerror(-r));
86aa7ba4
LP
344 return r;
345 }
346
347 r = sd_journal_get_cursor(j, &cursor);
348 if (r < 0) {
349 log_error("Failed to get cursor: %s", strerror(-r));
350 return r;
351 }
352
08ace05b
LP
353 fprintf(f, "%s [%s]\n",
354 format_timestamp(ts, sizeof(ts), realtime),
355 cursor);
86aa7ba4 356
a72b6353 357 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
31f7bf19
ZJS
358 const char *c;
359 int fieldlen;
360 c = memchr(data, '=', length);
361 if (!c) {
362 log_error("Invalid field.");
363 return -EINVAL;
364 }
365 fieldlen = c - (const char*) data;
86aa7ba4 366
31f7bf19
ZJS
367 if ((flags & OUTPUT_SHOW_ALL) || (length < PRINT_THRESHOLD && utf8_is_printable(data, length))) {
368 fprintf(f, " %.*s=", fieldlen, (const char*)data);
369 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
370 } else {
371 char bytes[FORMAT_BYTES_MAX];
86aa7ba4 372
31f7bf19
ZJS
373 fprintf(f, " %.*s=[%s blob data]\n",
374 (int) (c - (const char*) data),
375 (const char*) data,
376 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
377 }
86aa7ba4
LP
378 }
379
a72b6353
ZJS
380 if (r < 0)
381 return r;
382
d4205751
LP
383 if (flags & OUTPUT_CATALOG)
384 print_catalog(f, j);
385
86aa7ba4
LP
386 return 0;
387}
388
08ace05b
LP
389static int output_export(
390 FILE *f,
391 sd_journal *j,
392 OutputMode mode,
393 unsigned n_columns,
394 OutputFlags flags) {
395
86aa7ba4
LP
396 sd_id128_t boot_id;
397 char sid[33];
398 int r;
399 usec_t realtime, monotonic;
7fd1b19b 400 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
401 const void *data;
402 size_t length;
403
404 assert(j);
405
93b73b06
LP
406 sd_journal_set_data_threshold(j, 0);
407
86aa7ba4
LP
408 r = sd_journal_get_realtime_usec(j, &realtime);
409 if (r < 0) {
410 log_error("Failed to get realtime timestamp: %s", strerror(-r));
411 return r;
412 }
413
414 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
415 if (r < 0) {
416 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
417 return r;
418 }
419
420 r = sd_journal_get_cursor(j, &cursor);
421 if (r < 0) {
422 log_error("Failed to get cursor: %s", strerror(-r));
423 return r;
424 }
425
08ace05b
LP
426 fprintf(f,
427 "__CURSOR=%s\n"
428 "__REALTIME_TIMESTAMP=%llu\n"
429 "__MONOTONIC_TIMESTAMP=%llu\n"
430 "_BOOT_ID=%s\n",
431 cursor,
432 (unsigned long long) realtime,
433 (unsigned long long) monotonic,
434 sd_id128_to_string(boot_id, sid));
86aa7ba4 435
a72b6353 436 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
86aa7ba4 437
112301ae
LP
438 /* We already printed the boot id, from the data in
439 * the header, hence let's suppress it here */
440 if (length >= 9 &&
fd59d9f2 441 hasprefix(data, "_BOOT_ID="))
112301ae
LP
442 continue;
443
31f7bf19 444 if (!utf8_is_printable(data, length)) {
86aa7ba4
LP
445 const char *c;
446 uint64_t le64;
447
448 c = memchr(data, '=', length);
449 if (!c) {
450 log_error("Invalid field.");
451 return -EINVAL;
452 }
453
08ace05b
LP
454 fwrite(data, c - (const char*) data, 1, f);
455 fputc('\n', f);
86aa7ba4 456 le64 = htole64(length - (c - (const char*) data) - 1);
08ace05b
LP
457 fwrite(&le64, sizeof(le64), 1, f);
458 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
86aa7ba4 459 } else
08ace05b 460 fwrite(data, length, 1, f);
86aa7ba4 461
08ace05b 462 fputc('\n', f);
86aa7ba4
LP
463 }
464
a72b6353
ZJS
465 if (r < 0)
466 return r;
467
08ace05b 468 fputc('\n', f);
86aa7ba4
LP
469
470 return 0;
471}
472
240a5fe8 473void json_escape(
08ace05b
LP
474 FILE *f,
475 const char* p,
476 size_t l,
477 OutputFlags flags) {
478
479 assert(f);
480 assert(p);
481
93b73b06 482 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
08ace05b
LP
483
484 fputs("null", f);
485
31f7bf19 486 else if (!utf8_is_printable(p, l)) {
86aa7ba4
LP
487 bool not_first = false;
488
08ace05b 489 fputs("[ ", f);
86aa7ba4
LP
490
491 while (l > 0) {
492 if (not_first)
08ace05b 493 fprintf(f, ", %u", (uint8_t) *p);
86aa7ba4
LP
494 else {
495 not_first = true;
08ace05b 496 fprintf(f, "%u", (uint8_t) *p);
86aa7ba4
LP
497 }
498
499 p++;
500 l--;
501 }
502
08ace05b 503 fputs(" ]", f);
86aa7ba4 504 } else {
08ace05b 505 fputc('\"', f);
86aa7ba4
LP
506
507 while (l > 0) {
508 if (*p == '"' || *p == '\\') {
08ace05b
LP
509 fputc('\\', f);
510 fputc(*p, f);
31f7bf19
ZJS
511 } else if (*p == '\n')
512 fputs("\\n", f);
513 else if (*p < ' ')
08ace05b
LP
514 fprintf(f, "\\u%04x", *p);
515 else
516 fputc(*p, f);
86aa7ba4
LP
517
518 p++;
519 l--;
520 }
521
08ace05b 522 fputc('\"', f);
86aa7ba4
LP
523 }
524}
525
08ace05b
LP
526static int output_json(
527 FILE *f,
528 sd_journal *j,
529 OutputMode mode,
530 unsigned n_columns,
531 OutputFlags flags) {
532
86aa7ba4 533 uint64_t realtime, monotonic;
7fd1b19b 534 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
535 const void *data;
536 size_t length;
537 sd_id128_t boot_id;
2e729834 538 char sid[33], *k;
86aa7ba4 539 int r;
d99ae53a
LP
540 Hashmap *h = NULL;
541 bool done, separator;
86aa7ba4
LP
542
543 assert(j);
544
93b73b06
LP
545 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
546
86aa7ba4
LP
547 r = sd_journal_get_realtime_usec(j, &realtime);
548 if (r < 0) {
549 log_error("Failed to get realtime timestamp: %s", strerror(-r));
550 return r;
551 }
552
553 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
554 if (r < 0) {
555 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
556 return r;
557 }
558
559 r = sd_journal_get_cursor(j, &cursor);
560 if (r < 0) {
561 log_error("Failed to get cursor: %s", strerror(-r));
562 return r;
563 }
564
a6e87e90 565 if (mode == OUTPUT_JSON_PRETTY)
08ace05b
LP
566 fprintf(f,
567 "{\n"
568 "\t\"__CURSOR\" : \"%s\",\n"
569 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
570 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
571 "\t\"_BOOT_ID\" : \"%s\"",
572 cursor,
573 (unsigned long long) realtime,
574 (unsigned long long) monotonic,
575 sd_id128_to_string(boot_id, sid));
48383c25
LP
576 else {
577 if (mode == OUTPUT_JSON_SSE)
578 fputs("data: ", f);
579
08ace05b
LP
580 fprintf(f,
581 "{ \"__CURSOR\" : \"%s\", "
582 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
583 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
584 "\"_BOOT_ID\" : \"%s\"",
585 cursor,
586 (unsigned long long) realtime,
587 (unsigned long long) monotonic,
588 sd_id128_to_string(boot_id, sid));
48383c25 589 }
86aa7ba4 590
d99ae53a
LP
591 h = hashmap_new(string_hash_func, string_compare_func);
592 if (!h)
593 return -ENOMEM;
594
595 /* First round, iterate through the entry and count how often each field appears */
a72b6353 596 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
d99ae53a
LP
597 const char *eq;
598 char *n;
599 unsigned u;
86aa7ba4 600
112301ae
LP
601 if (length >= 9 &&
602 memcmp(data, "_BOOT_ID=", 9) == 0)
603 continue;
604
d99ae53a
LP
605 eq = memchr(data, '=', length);
606 if (!eq)
607 continue;
86aa7ba4 608
d99ae53a
LP
609 n = strndup(data, eq - (const char*) data);
610 if (!n) {
611 r = -ENOMEM;
612 goto finish;
613 }
a6e87e90 614
d99ae53a
LP
615 u = PTR_TO_UINT(hashmap_get(h, n));
616 if (u == 0) {
617 r = hashmap_put(h, n, UINT_TO_PTR(1));
618 if (r < 0) {
619 free(n);
620 goto finish;
621 }
622 } else {
623 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
624 free(n);
625 if (r < 0)
626 goto finish;
627 }
86aa7ba4
LP
628 }
629
a72b6353
ZJS
630 if (r < 0)
631 return r;
632
d99ae53a
LP
633 separator = true;
634 do {
635 done = true;
636
637 SD_JOURNAL_FOREACH_DATA(j, data, length) {
638 const char *eq;
639 char *kk, *n;
640 size_t m;
641 unsigned u;
642
643 /* We already printed the boot id, from the data in
644 * the header, hence let's suppress it here */
645 if (length >= 9 &&
646 memcmp(data, "_BOOT_ID=", 9) == 0)
647 continue;
648
649 eq = memchr(data, '=', length);
650 if (!eq)
651 continue;
652
653 if (separator) {
654 if (mode == OUTPUT_JSON_PRETTY)
655 fputs(",\n\t", f);
656 else
657 fputs(", ", f);
658 }
659
660 m = eq - (const char*) data;
661
662 n = strndup(data, m);
663 if (!n) {
664 r = -ENOMEM;
665 goto finish;
666 }
667
668 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
669 if (u == 0) {
670 /* We already printed this, let's jump to the next */
671 free(n);
672 separator = false;
673
674 continue;
675 } else if (u == 1) {
676 /* Field only appears once, output it directly */
677
678 json_escape(f, data, m, flags);
679 fputs(" : ", f);
680
681 json_escape(f, eq + 1, length - m - 1, flags);
682
683 hashmap_remove(h, n);
684 free(kk);
685 free(n);
686
687 separator = true;
688
689 continue;
690
691 } else {
692 /* Field appears multiple times, output it as array */
693 json_escape(f, data, m, flags);
694 fputs(" : [ ", f);
695 json_escape(f, eq + 1, length - m - 1, flags);
696
697 /* Iterate through the end of the list */
698
699 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
700 if (length < m + 1)
701 continue;
702
703 if (memcmp(data, n, m) != 0)
704 continue;
705
706 if (((const char*) data)[m] != '=')
707 continue;
708
709 fputs(", ", f);
710 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
711 }
712
713 fputs(" ]", f);
714
715 hashmap_remove(h, n);
716 free(kk);
717 free(n);
718
719 /* Iterate data fields form the beginning */
720 done = false;
721 separator = true;
722
723 break;
724 }
725 }
726
727 } while (!done);
728
a6e87e90 729 if (mode == OUTPUT_JSON_PRETTY)
08ace05b 730 fputs("\n}\n", f);
48383c25
LP
731 else if (mode == OUTPUT_JSON_SSE)
732 fputs("}\n\n", f);
a6e87e90 733 else
08ace05b 734 fputs(" }\n", f);
86aa7ba4 735
d99ae53a
LP
736 r = 0;
737
738finish:
739 while ((k = hashmap_steal_first_key(h)))
740 free(k);
741
742 hashmap_free(h);
743
744 return r;
86aa7ba4
LP
745}
746
08ace05b
LP
747static int output_cat(
748 FILE *f,
749 sd_journal *j,
750 OutputMode mode,
751 unsigned n_columns,
752 OutputFlags flags) {
753
d3f2bdbf
LP
754 const void *data;
755 size_t l;
756 int r;
757
758 assert(j);
08ace05b 759 assert(f);
d3f2bdbf 760
93b73b06
LP
761 sd_journal_set_data_threshold(j, 0);
762
d3f2bdbf
LP
763 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
764 if (r < 0) {
c198300f
LP
765 /* An entry without MESSAGE=? */
766 if (r == -ENOENT)
767 return 0;
768
d3f2bdbf
LP
769 log_error("Failed to get data: %s", strerror(-r));
770 return r;
771 }
772
773 assert(l >= 8);
774
08ace05b
LP
775 fwrite((const char*) data + 8, 1, l - 8, f);
776 fputc('\n', f);
d3f2bdbf
LP
777
778 return 0;
779}
780
08ace05b
LP
781static int (*output_funcs[_OUTPUT_MODE_MAX])(
782 FILE *f,
783 sd_journal*j,
784 OutputMode mode,
785 unsigned n_columns,
786 OutputFlags flags) = {
787
a6e87e90
LP
788 [OUTPUT_SHORT] = output_short,
789 [OUTPUT_SHORT_MONOTONIC] = output_short,
86aa7ba4
LP
790 [OUTPUT_VERBOSE] = output_verbose,
791 [OUTPUT_EXPORT] = output_export,
d3f2bdbf 792 [OUTPUT_JSON] = output_json,
a6e87e90 793 [OUTPUT_JSON_PRETTY] = output_json,
48383c25 794 [OUTPUT_JSON_SSE] = output_json,
d3f2bdbf 795 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
796};
797
08ace05b
LP
798int output_journal(
799 FILE *f,
800 sd_journal *j,
801 OutputMode mode,
802 unsigned n_columns,
803 OutputFlags flags) {
804
e268b81e 805 int ret;
df50185b 806 assert(mode >= 0);
86aa7ba4
LP
807 assert(mode < _OUTPUT_MODE_MAX);
808
34a35ece
LP
809 if (n_columns <= 0)
810 n_columns = columns();
811
08ace05b 812 ret = output_funcs[mode](f, j, mode, n_columns, flags);
e268b81e
BP
813 fflush(stdout);
814 return ret;
86aa7ba4
LP
815}
816
1a6c43e9
MT
817static int show_journal(FILE *f,
818 sd_journal *j,
819 OutputMode mode,
820 unsigned n_columns,
821 usec_t not_before,
822 unsigned how_many,
823 OutputFlags flags) {
86aa7ba4 824
86aa7ba4 825 int r;
df50185b
LP
826 unsigned line = 0;
827 bool need_seek = false;
085d7120 828 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 829
1a6c43e9 830 assert(j);
df50185b
LP
831 assert(mode >= 0);
832 assert(mode < _OUTPUT_MODE_MAX);
1946b0bd
LP
833
834 /* Seek to end */
86aa7ba4
LP
835 r = sd_journal_seek_tail(j);
836 if (r < 0)
837 goto finish;
838
df50185b
LP
839 r = sd_journal_previous_skip(j, how_many);
840 if (r < 0)
841 goto finish;
86aa7ba4 842
df50185b
LP
843 for (;;) {
844 for (;;) {
845 usec_t usec;
846
847 if (need_seek) {
848 r = sd_journal_next(j);
849 if (r < 0)
850 goto finish;
851 }
852
853 if (r == 0)
854 break;
86aa7ba4 855
df50185b
LP
856 need_seek = true;
857
858 if (not_before > 0) {
859 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
860
861 /* -ESTALE is returned if the
862 timestamp is not from this boot */
863 if (r == -ESTALE)
864 continue;
865 else if (r < 0)
866 goto finish;
867
868 if (usec < not_before)
869 continue;
870 }
871
872 line ++;
873
08ace05b 874 r = output_journal(f, j, mode, n_columns, flags);
df50185b
LP
875 if (r < 0)
876 goto finish;
877 }
878
08984293
LP
879 if (warn_cutoff && line < how_many && not_before > 0) {
880 sd_id128_t boot_id;
881 usec_t cutoff;
882
883 /* Check whether the cutoff line is too early */
884
885 r = sd_id128_get_boot(&boot_id);
886 if (r < 0)
887 goto finish;
888
889 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
890 if (r < 0)
891 goto finish;
892
b59866ae 893 if (r > 0 && not_before < cutoff)
08ace05b 894 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
08984293
LP
895
896 warn_cutoff = false;
897 }
898
085d7120 899 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
900 break;
901
e02d1cf7 902 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
903 if (r < 0)
904 goto finish;
905
86aa7ba4
LP
906 }
907
1a6c43e9
MT
908finish:
909 return r;
910}
911
886a64fe 912int add_matches_for_unit(sd_journal *j, const char *unit) {
1a6c43e9 913 int r;
886a64fe 914 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
1a6c43e9 915
886a64fe 916 assert(j);
1a6c43e9
MT
917 assert(unit);
918
1a6c43e9
MT
919 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
920 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
886a64fe
ZJS
921 asprintf(&m3, "UNIT=%s", unit) < 0)
922 return -ENOMEM;
1a6c43e9 923
886a64fe
ZJS
924 (void)(
925 /* Look for messages from the service itself */
926 (r = sd_journal_add_match(j, m1, 0)) ||
927
928 /* Look for coredumps of the service */
929 (r = sd_journal_add_disjunction(j)) ||
930 (r = sd_journal_add_match(j,
931 "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
932 (r = sd_journal_add_match(j, m2, 0)) ||
933
934 /* Look for messages from PID 1 about this service */
935 (r = sd_journal_add_disjunction(j)) ||
936 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
937 (r = sd_journal_add_match(j, m3, 0))
938 );
939 return r;
940}
1a6c43e9 941
886a64fe
ZJS
942int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
943 int r;
944 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
1a6c43e9 945
886a64fe
ZJS
946 assert(j);
947 assert(unit);
1a6c43e9 948
886a64fe
ZJS
949 if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 ||
950 asprintf(&m2, "USER_UNIT=%s", unit) < 0 ||
951 asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 ||
952 asprintf(&m4, "_UID=%d", uid) < 0)
953 return -ENOMEM;
1a6c43e9 954
886a64fe
ZJS
955 (void) (
956 /* Look for messages from the user service itself */
957 (r = sd_journal_add_match(j, m1, 0)) ||
958 (r = sd_journal_add_match(j, m4, 0)) ||
959
960 /* Look for messages from systemd about this service */
961 (r = sd_journal_add_disjunction(j)) ||
962 (r = sd_journal_add_match(j, m2, 0)) ||
963 (r = sd_journal_add_match(j, m4, 0)) ||
964
965 /* Look for coredumps of the service */
966 (r = sd_journal_add_disjunction(j)) ||
967 (r = sd_journal_add_match(j, m3, 0)) ||
968 (r = sd_journal_add_match(j, m4, 0))
969 );
1a6c43e9
MT
970 return r;
971}
972
5ec76417
ZJS
973int add_match_this_boot(sd_journal *j) {
974 char match[9+32+1] = "_BOOT_ID=";
975 sd_id128_t boot_id;
976 int r;
977
978 assert(j);
979
980 r = sd_id128_get_boot(&boot_id);
981 if (r < 0) {
982 log_error("Failed to get boot id: %s", strerror(-r));
983 return r;
984 }
985
986 sd_id128_to_string(boot_id, match + 9);
987 r = sd_journal_add_match(j, match, strlen(match));
988 if (r < 0) {
989 log_error("Failed to add match: %s", strerror(-r));
990 return r;
991 }
992
993 r = sd_journal_add_conjunction(j);
994 if (r < 0)
995 return r;
996
997 return 0;
998}
999
886a64fe 1000int show_journal_by_unit(
1a6c43e9
MT
1001 FILE *f,
1002 const char *unit,
1003 OutputMode mode,
1004 unsigned n_columns,
1005 usec_t not_before,
1006 unsigned how_many,
1007 uid_t uid,
886a64fe
ZJS
1008 OutputFlags flags,
1009 bool system) {
1a6c43e9 1010
7fd1b19b 1011 _cleanup_journal_close_ sd_journal*j = NULL;
1a6c43e9 1012 int r;
a688baa8 1013 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1a6c43e9
MT
1014
1015 assert(mode >= 0);
1016 assert(mode < _OUTPUT_MODE_MAX);
1017 assert(unit);
1018
1a6c43e9
MT
1019 if (how_many <= 0)
1020 return 0;
1021
886a64fe 1022 r = sd_journal_open(&j, jflags);
f9045468 1023 if (r < 0)
763c7aa2 1024 return r;
f9045468 1025
5ec76417
ZJS
1026 r = add_match_this_boot(j);
1027 if (r < 0)
1028 return r;
1029
886a64fe
ZJS
1030 if (system)
1031 r = add_matches_for_unit(j, unit);
1032 else
1033 r = add_matches_for_user_unit(j, unit, uid);
1a6c43e9 1034 if (r < 0)
763c7aa2 1035 return r;
1a6c43e9 1036
5ec76417
ZJS
1037 log_debug("Journal filter: %s", journal_make_match_string(j));
1038
1a6c43e9
MT
1039 r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
1040 if (r < 0)
763c7aa2 1041 return r;
86aa7ba4 1042
763c7aa2 1043 return 0;
86aa7ba4 1044}
df50185b
LP
1045
1046static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1047 [OUTPUT_SHORT] = "short",
67a12205 1048 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
1049 [OUTPUT_VERBOSE] = "verbose",
1050 [OUTPUT_EXPORT] = "export",
d3f2bdbf 1051 [OUTPUT_JSON] = "json",
a6e87e90 1052 [OUTPUT_JSON_PRETTY] = "json-pretty",
48383c25 1053 [OUTPUT_JSON_SSE] = "json-sse",
d3f2bdbf 1054 [OUTPUT_CAT] = "cat"
df50185b
LP
1055};
1056
1057DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);