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