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