]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/logs-show.c
Add hasprefix macro to check prefixes of fixed length
[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(p, l))
99 return false;
100
101 return true;
102 }
103
104 static void print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputMode flags, int priority, const char* message, size_t message_len) {
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
126 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || prefix + len + 1 < n_columns)
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
146 static int output_short(
147 FILE *f,
148 sd_journal *j,
149 OutputMode mode,
150 unsigned n_columns,
151 OutputFlags flags) {
152
153 int r;
154 const void *data;
155 size_t length;
156 size_t n = 0;
157 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
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;
160
161 assert(f);
162 assert(j);
163
164 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
165
166 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
167
168 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
169 if (r < 0)
170 return r;
171 else if (r > 0)
172 continue;
173
174 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
175 if (r < 0)
176 return r;
177 else if (r > 0)
178 continue;
179
180 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
181 if (r < 0)
182 return r;
183 else if (r > 0)
184 continue;
185
186 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
187 if (r < 0)
188 return r;
189 else if (r > 0)
190 continue;
191
192 r = parse_field(data, length, "_PID=", &pid, &pid_len);
193 if (r < 0)
194 return r;
195 else if (r > 0)
196 continue;
197
198 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
199 if (r < 0)
200 return r;
201 else if (r > 0)
202 continue;
203
204 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
205 if (r < 0)
206 return r;
207 else if (r > 0)
208 continue;
209
210 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
211 if (r < 0)
212 return r;
213 else if (r > 0)
214 continue;
215
216 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
217 if (r < 0)
218 return r;
219 }
220
221 if (r < 0)
222 return r;
223
224 if (!message)
225 return 0;
226
227 if (!(flags & OUTPUT_SHOW_ALL))
228 strip_tab_ansi(&message, &message_len);
229
230 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
231 p = *priority - '0';
232
233 if (mode == OUTPUT_SHORT_MONOTONIC) {
234 uint64_t t;
235 sd_id128_t boot_id;
236
237 r = -ENOENT;
238
239 if (monotonic)
240 r = safe_atou64(monotonic, &t);
241
242 if (r < 0)
243 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
244
245 if (r < 0) {
246 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
247 return r;
248 }
249
250 fprintf(f, "[%5llu.%06llu]",
251 (unsigned long long) (t / USEC_PER_SEC),
252 (unsigned long long) (t % USEC_PER_SEC));
253
254 n += 1 + 5 + 1 + 6 + 1;
255
256 } else {
257 char buf[64];
258 uint64_t x;
259 time_t t;
260 struct tm tm;
261
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);
269
270 if (r < 0) {
271 log_error("Failed to get realtime timestamp: %s", strerror(-r));
272 return r;
273 }
274
275 t = (time_t) (x / USEC_PER_SEC);
276 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
277 log_error("Failed to format time.");
278 return r;
279 }
280
281 fputs(buf, f);
282 n += strlen(buf);
283 }
284
285 if (hostname && shall_print(hostname, hostname_len, flags)) {
286 fprintf(f, " %.*s", (int) hostname_len, hostname);
287 n += hostname_len + 1;
288 }
289
290 if (identifier && shall_print(identifier, identifier_len, flags)) {
291 fprintf(f, " %.*s", (int) identifier_len, identifier);
292 n += identifier_len + 1;
293 } else if (comm && shall_print(comm, comm_len, flags)) {
294 fprintf(f, " %.*s", (int) comm_len, comm);
295 n += comm_len + 1;
296 } else
297 fputc(' ', f);
298
299 if (pid && shall_print(pid, pid_len, flags)) {
300 fprintf(f, "[%.*s]", (int) pid_len, pid);
301 n += pid_len + 2;
302 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
303 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
304 n += fake_pid_len + 2;
305 }
306
307 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
308 char bytes[FORMAT_BYTES_MAX];
309 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
310 } else {
311 fputs(": ", f);
312 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
313 }
314
315 if (flags & OUTPUT_CATALOG)
316 print_catalog(f, j);
317
318 return 0;
319 }
320
321 static int output_verbose(
322 FILE *f,
323 sd_journal *j,
324 OutputMode mode,
325 unsigned n_columns,
326 OutputFlags flags) {
327
328 const void *data;
329 size_t length;
330 _cleanup_free_ char *cursor = NULL;
331 uint64_t realtime;
332 char ts[FORMAT_TIMESTAMP_MAX];
333 int r;
334
335 assert(f);
336 assert(j);
337
338 sd_journal_set_data_threshold(j, 0);
339
340 r = sd_journal_get_realtime_usec(j, &realtime);
341 if (r < 0) {
342 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
343 "Failed to get realtime timestamp: %s", strerror(-r));
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
353 fprintf(f, "%s [%s]\n",
354 format_timestamp(ts, sizeof(ts), realtime),
355 cursor);
356
357 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
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;
366
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];
372
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 }
378 }
379
380 if (r < 0)
381 return r;
382
383 if (flags & OUTPUT_CATALOG)
384 print_catalog(f, j);
385
386 return 0;
387 }
388
389 static int output_export(
390 FILE *f,
391 sd_journal *j,
392 OutputMode mode,
393 unsigned n_columns,
394 OutputFlags flags) {
395
396 sd_id128_t boot_id;
397 char sid[33];
398 int r;
399 usec_t realtime, monotonic;
400 _cleanup_free_ char *cursor = NULL;
401 const void *data;
402 size_t length;
403
404 assert(j);
405
406 sd_journal_set_data_threshold(j, 0);
407
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
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));
435
436 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
437
438 /* We already printed the boot id, from the data in
439 * the header, hence let's suppress it here */
440 if (length >= 9 &&
441 hasprefix(data, "_BOOT_ID="))
442 continue;
443
444 if (!utf8_is_printable(data, length)) {
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
454 fwrite(data, c - (const char*) data, 1, f);
455 fputc('\n', f);
456 le64 = htole64(length - (c - (const char*) data) - 1);
457 fwrite(&le64, sizeof(le64), 1, f);
458 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
459 } else
460 fwrite(data, length, 1, f);
461
462 fputc('\n', f);
463 }
464
465 if (r < 0)
466 return r;
467
468 fputc('\n', f);
469
470 return 0;
471 }
472
473 void json_escape(
474 FILE *f,
475 const char* p,
476 size_t l,
477 OutputFlags flags) {
478
479 assert(f);
480 assert(p);
481
482 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
483
484 fputs("null", f);
485
486 else if (!utf8_is_printable(p, l)) {
487 bool not_first = false;
488
489 fputs("[ ", f);
490
491 while (l > 0) {
492 if (not_first)
493 fprintf(f, ", %u", (uint8_t) *p);
494 else {
495 not_first = true;
496 fprintf(f, "%u", (uint8_t) *p);
497 }
498
499 p++;
500 l--;
501 }
502
503 fputs(" ]", f);
504 } else {
505 fputc('\"', f);
506
507 while (l > 0) {
508 if (*p == '"' || *p == '\\') {
509 fputc('\\', f);
510 fputc(*p, f);
511 } else if (*p == '\n')
512 fputs("\\n", f);
513 else if (*p < ' ')
514 fprintf(f, "\\u%04x", *p);
515 else
516 fputc(*p, f);
517
518 p++;
519 l--;
520 }
521
522 fputc('\"', f);
523 }
524 }
525
526 static int output_json(
527 FILE *f,
528 sd_journal *j,
529 OutputMode mode,
530 unsigned n_columns,
531 OutputFlags flags) {
532
533 uint64_t realtime, monotonic;
534 _cleanup_free_ char *cursor = NULL;
535 const void *data;
536 size_t length;
537 sd_id128_t boot_id;
538 char sid[33], *k;
539 int r;
540 Hashmap *h = NULL;
541 bool done, separator;
542
543 assert(j);
544
545 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
546
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
565 if (mode == OUTPUT_JSON_PRETTY)
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));
576 else {
577 if (mode == OUTPUT_JSON_SSE)
578 fputs("data: ", f);
579
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));
589 }
590
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 */
596 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
597 const char *eq;
598 char *n;
599 unsigned u;
600
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 n = strndup(data, eq - (const char*) data);
610 if (!n) {
611 r = -ENOMEM;
612 goto finish;
613 }
614
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 }
628 }
629
630 if (r < 0)
631 return r;
632
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
729 if (mode == OUTPUT_JSON_PRETTY)
730 fputs("\n}\n", f);
731 else if (mode == OUTPUT_JSON_SSE)
732 fputs("}\n\n", f);
733 else
734 fputs(" }\n", f);
735
736 r = 0;
737
738 finish:
739 while ((k = hashmap_steal_first_key(h)))
740 free(k);
741
742 hashmap_free(h);
743
744 return r;
745 }
746
747 static int output_cat(
748 FILE *f,
749 sd_journal *j,
750 OutputMode mode,
751 unsigned n_columns,
752 OutputFlags flags) {
753
754 const void *data;
755 size_t l;
756 int r;
757
758 assert(j);
759 assert(f);
760
761 sd_journal_set_data_threshold(j, 0);
762
763 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
764 if (r < 0) {
765 /* An entry without MESSAGE=? */
766 if (r == -ENOENT)
767 return 0;
768
769 log_error("Failed to get data: %s", strerror(-r));
770 return r;
771 }
772
773 assert(l >= 8);
774
775 fwrite((const char*) data + 8, 1, l - 8, f);
776 fputc('\n', f);
777
778 return 0;
779 }
780
781 static int (*output_funcs[_OUTPUT_MODE_MAX])(
782 FILE *f,
783 sd_journal*j,
784 OutputMode mode,
785 unsigned n_columns,
786 OutputFlags flags) = {
787
788 [OUTPUT_SHORT] = output_short,
789 [OUTPUT_SHORT_MONOTONIC] = output_short,
790 [OUTPUT_VERBOSE] = output_verbose,
791 [OUTPUT_EXPORT] = output_export,
792 [OUTPUT_JSON] = output_json,
793 [OUTPUT_JSON_PRETTY] = output_json,
794 [OUTPUT_JSON_SSE] = output_json,
795 [OUTPUT_CAT] = output_cat
796 };
797
798 int output_journal(
799 FILE *f,
800 sd_journal *j,
801 OutputMode mode,
802 unsigned n_columns,
803 OutputFlags flags) {
804
805 int ret;
806 assert(mode >= 0);
807 assert(mode < _OUTPUT_MODE_MAX);
808
809 if (n_columns <= 0)
810 n_columns = columns();
811
812 ret = output_funcs[mode](f, j, mode, n_columns, flags);
813 fflush(stdout);
814 return ret;
815 }
816
817 static 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) {
824
825 int r;
826 unsigned line = 0;
827 bool need_seek = false;
828 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
829
830 assert(j);
831 assert(mode >= 0);
832 assert(mode < _OUTPUT_MODE_MAX);
833
834 /* Seek to end */
835 r = sd_journal_seek_tail(j);
836 if (r < 0)
837 goto finish;
838
839 r = sd_journal_previous_skip(j, how_many);
840 if (r < 0)
841 goto finish;
842
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;
855
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
874 r = output_journal(f, j, mode, n_columns, flags);
875 if (r < 0)
876 goto finish;
877 }
878
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
893 if (r > 0 && not_before < cutoff)
894 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
895
896 warn_cutoff = false;
897 }
898
899 if (!(flags & OUTPUT_FOLLOW))
900 break;
901
902 r = sd_journal_wait(j, (usec_t) -1);
903 if (r < 0)
904 goto finish;
905
906 }
907
908 finish:
909 return r;
910 }
911
912 int add_matches_for_unit(sd_journal *j, const char *unit) {
913 int r;
914 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
915
916 assert(j);
917 assert(unit);
918
919 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
920 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
921 asprintf(&m3, "UNIT=%s", unit) < 0)
922 return -ENOMEM;
923
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 }
941
942 int 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;
945
946 assert(j);
947 assert(unit);
948
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;
954
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 );
970 return r;
971 }
972
973 int 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
1000 int show_journal_by_unit(
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,
1008 OutputFlags flags,
1009 bool system) {
1010
1011 _cleanup_journal_close_ sd_journal*j = NULL;
1012 int r;
1013 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1014
1015 assert(mode >= 0);
1016 assert(mode < _OUTPUT_MODE_MAX);
1017 assert(unit);
1018
1019 if (how_many <= 0)
1020 return 0;
1021
1022 r = sd_journal_open(&j, jflags);
1023 if (r < 0)
1024 return r;
1025
1026 r = add_match_this_boot(j);
1027 if (r < 0)
1028 return r;
1029
1030 if (system)
1031 r = add_matches_for_unit(j, unit);
1032 else
1033 r = add_matches_for_user_unit(j, unit, uid);
1034 if (r < 0)
1035 return r;
1036
1037 log_debug("Journal filter: %s", journal_make_match_string(j));
1038
1039 r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
1040 if (r < 0)
1041 return r;
1042
1043 return 0;
1044 }
1045
1046 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1047 [OUTPUT_SHORT] = "short",
1048 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1049 [OUTPUT_VERBOSE] = "verbose",
1050 [OUTPUT_EXPORT] = "export",
1051 [OUTPUT_JSON] = "json",
1052 [OUTPUT_JSON_PRETTY] = "json-pretty",
1053 [OUTPUT_JSON_SSE] = "json-sse",
1054 [OUTPUT_CAT] = "cat"
1055 };
1056
1057 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);