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