]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/logs-show.c
logs-show.c: show all messages for a slice
[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, OutputFlags 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 not-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 + PRINT_CHAR_THRESHOLD;
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 * threshold, 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
322 switch(mode) {
323 case OUTPUT_SHORT_ISO:
324 r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", localtime_r(&t, &tm));
325 break;
326 case OUTPUT_SHORT_PRECISE:
327 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
328 if (r > 0) {
329 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
330 ".%06llu", x % USEC_PER_SEC);
331 }
332 break;
333 default:
334 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
335 }
336
337 if (r <= 0) {
338 log_error("Failed to format time.");
339 return -EINVAL;
340 }
341
342 fputs(buf, f);
343 n += strlen(buf);
344 }
345
346 if (hostname && shall_print(hostname, hostname_len, flags)) {
347 fprintf(f, " %.*s", (int) hostname_len, hostname);
348 n += hostname_len + 1;
349 }
350
351 if (identifier && shall_print(identifier, identifier_len, flags)) {
352 fprintf(f, " %.*s", (int) identifier_len, identifier);
353 n += identifier_len + 1;
354 } else if (comm && shall_print(comm, comm_len, flags)) {
355 fprintf(f, " %.*s", (int) comm_len, comm);
356 n += comm_len + 1;
357 } else
358 fputc(' ', f);
359
360 if (pid && shall_print(pid, pid_len, flags)) {
361 fprintf(f, "[%.*s]", (int) pid_len, pid);
362 n += pid_len + 2;
363 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
364 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
365 n += fake_pid_len + 2;
366 }
367
368 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
369 char bytes[FORMAT_BYTES_MAX];
370 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
371 } else {
372 fputs(": ", f);
373 ellipsized |=
374 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
375 }
376
377 if (flags & OUTPUT_CATALOG)
378 print_catalog(f, j);
379
380 return ellipsized;
381 }
382
383 static int output_verbose(
384 FILE *f,
385 sd_journal *j,
386 OutputMode mode,
387 unsigned n_columns,
388 OutputFlags flags) {
389
390 const void *data;
391 size_t length;
392 _cleanup_free_ char *cursor = NULL;
393 uint64_t realtime;
394 char ts[FORMAT_TIMESTAMP_MAX + 7];
395 int r;
396
397 assert(f);
398 assert(j);
399
400 sd_journal_set_data_threshold(j, 0);
401
402 r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
403 if (r == -ENOENT)
404 log_debug("Source realtime timestamp not found");
405 else if (r < 0) {
406 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
407 "Failed to get source realtime timestamp: %s", strerror(-r));
408 return r;
409 } else {
410 _cleanup_free_ char *value = NULL;
411 size_t size;
412
413 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
414 if (r < 0)
415 log_debug("_SOURCE_REALTIME_TIMESTAMP invalid: %s", strerror(-r));
416 else {
417 r = safe_atou64(value, &realtime);
418 if (r < 0)
419 log_debug("Failed to parse realtime timestamp: %s",
420 strerror(-r));
421 }
422 }
423
424 if (r < 0) {
425 r = sd_journal_get_realtime_usec(j, &realtime);
426 if (r < 0) {
427 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
428 "Failed to get realtime timestamp: %s", strerror(-r));
429 return r;
430 }
431 }
432
433 r = sd_journal_get_cursor(j, &cursor);
434 if (r < 0) {
435 log_error("Failed to get cursor: %s", strerror(-r));
436 return r;
437 }
438
439 fprintf(f, "%s [%s]\n",
440 format_timestamp_us(ts, sizeof(ts), realtime),
441 cursor);
442
443 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
444 const char *c;
445 int fieldlen;
446 const char *on = "", *off = "";
447
448 c = memchr(data, '=', length);
449 if (!c) {
450 log_error("Invalid field.");
451 return -EINVAL;
452 }
453 fieldlen = c - (const char*) data;
454
455 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
456 on = ANSI_HIGHLIGHT_ON;
457 off = ANSI_HIGHLIGHT_OFF;
458 }
459
460 if (flags & OUTPUT_SHOW_ALL ||
461 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
462 && utf8_is_printable(data, length))) {
463 fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
464 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
465 fputs(off, f);
466 } else {
467 char bytes[FORMAT_BYTES_MAX];
468
469 fprintf(f, " %s%.*s=[%s blob data]%s\n",
470 on,
471 (int) (c - (const char*) data),
472 (const char*) data,
473 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
474 off);
475 }
476 }
477
478 if (r < 0)
479 return r;
480
481 if (flags & OUTPUT_CATALOG)
482 print_catalog(f, j);
483
484 return 0;
485 }
486
487 static int output_export(
488 FILE *f,
489 sd_journal *j,
490 OutputMode mode,
491 unsigned n_columns,
492 OutputFlags flags) {
493
494 sd_id128_t boot_id;
495 char sid[33];
496 int r;
497 usec_t realtime, monotonic;
498 _cleanup_free_ char *cursor = NULL;
499 const void *data;
500 size_t length;
501
502 assert(j);
503
504 sd_journal_set_data_threshold(j, 0);
505
506 r = sd_journal_get_realtime_usec(j, &realtime);
507 if (r < 0) {
508 log_error("Failed to get realtime timestamp: %s", strerror(-r));
509 return r;
510 }
511
512 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
513 if (r < 0) {
514 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
515 return r;
516 }
517
518 r = sd_journal_get_cursor(j, &cursor);
519 if (r < 0) {
520 log_error("Failed to get cursor: %s", strerror(-r));
521 return r;
522 }
523
524 fprintf(f,
525 "__CURSOR=%s\n"
526 "__REALTIME_TIMESTAMP=%llu\n"
527 "__MONOTONIC_TIMESTAMP=%llu\n"
528 "_BOOT_ID=%s\n",
529 cursor,
530 (unsigned long long) realtime,
531 (unsigned long long) monotonic,
532 sd_id128_to_string(boot_id, sid));
533
534 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
535
536 /* We already printed the boot id, from the data in
537 * the header, hence let's suppress it here */
538 if (length >= 9 &&
539 startswith(data, "_BOOT_ID="))
540 continue;
541
542 if (!utf8_is_printable(data, length)) {
543 const char *c;
544 uint64_t le64;
545
546 c = memchr(data, '=', length);
547 if (!c) {
548 log_error("Invalid field.");
549 return -EINVAL;
550 }
551
552 fwrite(data, c - (const char*) data, 1, f);
553 fputc('\n', f);
554 le64 = htole64(length - (c - (const char*) data) - 1);
555 fwrite(&le64, sizeof(le64), 1, f);
556 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
557 } else
558 fwrite(data, length, 1, f);
559
560 fputc('\n', f);
561 }
562
563 if (r < 0)
564 return r;
565
566 fputc('\n', f);
567
568 return 0;
569 }
570
571 void json_escape(
572 FILE *f,
573 const char* p,
574 size_t l,
575 OutputFlags flags) {
576
577 assert(f);
578 assert(p);
579
580 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
581
582 fputs("null", f);
583
584 else if (!utf8_is_printable(p, l)) {
585 bool not_first = false;
586
587 fputs("[ ", f);
588
589 while (l > 0) {
590 if (not_first)
591 fprintf(f, ", %u", (uint8_t) *p);
592 else {
593 not_first = true;
594 fprintf(f, "%u", (uint8_t) *p);
595 }
596
597 p++;
598 l--;
599 }
600
601 fputs(" ]", f);
602 } else {
603 fputc('\"', f);
604
605 while (l > 0) {
606 if (*p == '"' || *p == '\\') {
607 fputc('\\', f);
608 fputc(*p, f);
609 } else if (*p == '\n')
610 fputs("\\n", f);
611 else if (*p < ' ')
612 fprintf(f, "\\u%04x", *p);
613 else
614 fputc(*p, f);
615
616 p++;
617 l--;
618 }
619
620 fputc('\"', f);
621 }
622 }
623
624 static int output_json(
625 FILE *f,
626 sd_journal *j,
627 OutputMode mode,
628 unsigned n_columns,
629 OutputFlags flags) {
630
631 uint64_t realtime, monotonic;
632 _cleanup_free_ char *cursor = NULL;
633 const void *data;
634 size_t length;
635 sd_id128_t boot_id;
636 char sid[33], *k;
637 int r;
638 Hashmap *h = NULL;
639 bool done, separator;
640
641 assert(j);
642
643 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
644
645 r = sd_journal_get_realtime_usec(j, &realtime);
646 if (r < 0) {
647 log_error("Failed to get realtime timestamp: %s", strerror(-r));
648 return r;
649 }
650
651 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
652 if (r < 0) {
653 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
654 return r;
655 }
656
657 r = sd_journal_get_cursor(j, &cursor);
658 if (r < 0) {
659 log_error("Failed to get cursor: %s", strerror(-r));
660 return r;
661 }
662
663 if (mode == OUTPUT_JSON_PRETTY)
664 fprintf(f,
665 "{\n"
666 "\t\"__CURSOR\" : \"%s\",\n"
667 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
668 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
669 "\t\"_BOOT_ID\" : \"%s\"",
670 cursor,
671 (unsigned long long) realtime,
672 (unsigned long long) monotonic,
673 sd_id128_to_string(boot_id, sid));
674 else {
675 if (mode == OUTPUT_JSON_SSE)
676 fputs("data: ", f);
677
678 fprintf(f,
679 "{ \"__CURSOR\" : \"%s\", "
680 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
681 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
682 "\"_BOOT_ID\" : \"%s\"",
683 cursor,
684 (unsigned long long) realtime,
685 (unsigned long long) monotonic,
686 sd_id128_to_string(boot_id, sid));
687 }
688
689 h = hashmap_new(string_hash_func, string_compare_func);
690 if (!h)
691 return -ENOMEM;
692
693 /* First round, iterate through the entry and count how often each field appears */
694 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
695 const char *eq;
696 char *n;
697 unsigned u;
698
699 if (length >= 9 &&
700 memcmp(data, "_BOOT_ID=", 9) == 0)
701 continue;
702
703 eq = memchr(data, '=', length);
704 if (!eq)
705 continue;
706
707 n = strndup(data, eq - (const char*) data);
708 if (!n) {
709 r = -ENOMEM;
710 goto finish;
711 }
712
713 u = PTR_TO_UINT(hashmap_get(h, n));
714 if (u == 0) {
715 r = hashmap_put(h, n, UINT_TO_PTR(1));
716 if (r < 0) {
717 free(n);
718 goto finish;
719 }
720 } else {
721 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
722 free(n);
723 if (r < 0)
724 goto finish;
725 }
726 }
727
728 if (r < 0)
729 return r;
730
731 separator = true;
732 do {
733 done = true;
734
735 SD_JOURNAL_FOREACH_DATA(j, data, length) {
736 const char *eq;
737 char *kk, *n;
738 size_t m;
739 unsigned u;
740
741 /* We already printed the boot id, from the data in
742 * the header, hence let's suppress it here */
743 if (length >= 9 &&
744 memcmp(data, "_BOOT_ID=", 9) == 0)
745 continue;
746
747 eq = memchr(data, '=', length);
748 if (!eq)
749 continue;
750
751 if (separator) {
752 if (mode == OUTPUT_JSON_PRETTY)
753 fputs(",\n\t", f);
754 else
755 fputs(", ", f);
756 }
757
758 m = eq - (const char*) data;
759
760 n = strndup(data, m);
761 if (!n) {
762 r = -ENOMEM;
763 goto finish;
764 }
765
766 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
767 if (u == 0) {
768 /* We already printed this, let's jump to the next */
769 free(n);
770 separator = false;
771
772 continue;
773 } else if (u == 1) {
774 /* Field only appears once, output it directly */
775
776 json_escape(f, data, m, flags);
777 fputs(" : ", f);
778
779 json_escape(f, eq + 1, length - m - 1, flags);
780
781 hashmap_remove(h, n);
782 free(kk);
783 free(n);
784
785 separator = true;
786
787 continue;
788
789 } else {
790 /* Field appears multiple times, output it as array */
791 json_escape(f, data, m, flags);
792 fputs(" : [ ", f);
793 json_escape(f, eq + 1, length - m - 1, flags);
794
795 /* Iterate through the end of the list */
796
797 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
798 if (length < m + 1)
799 continue;
800
801 if (memcmp(data, n, m) != 0)
802 continue;
803
804 if (((const char*) data)[m] != '=')
805 continue;
806
807 fputs(", ", f);
808 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
809 }
810
811 fputs(" ]", f);
812
813 hashmap_remove(h, n);
814 free(kk);
815 free(n);
816
817 /* Iterate data fields form the beginning */
818 done = false;
819 separator = true;
820
821 break;
822 }
823 }
824
825 } while (!done);
826
827 if (mode == OUTPUT_JSON_PRETTY)
828 fputs("\n}\n", f);
829 else if (mode == OUTPUT_JSON_SSE)
830 fputs("}\n\n", f);
831 else
832 fputs(" }\n", f);
833
834 r = 0;
835
836 finish:
837 while ((k = hashmap_steal_first_key(h)))
838 free(k);
839
840 hashmap_free(h);
841
842 return r;
843 }
844
845 static int output_cat(
846 FILE *f,
847 sd_journal *j,
848 OutputMode mode,
849 unsigned n_columns,
850 OutputFlags flags) {
851
852 const void *data;
853 size_t l;
854 int r;
855
856 assert(j);
857 assert(f);
858
859 sd_journal_set_data_threshold(j, 0);
860
861 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
862 if (r < 0) {
863 /* An entry without MESSAGE=? */
864 if (r == -ENOENT)
865 return 0;
866
867 log_error("Failed to get data: %s", strerror(-r));
868 return r;
869 }
870
871 assert(l >= 8);
872
873 fwrite((const char*) data + 8, 1, l - 8, f);
874 fputc('\n', f);
875
876 return 0;
877 }
878
879 static int (*output_funcs[_OUTPUT_MODE_MAX])(
880 FILE *f,
881 sd_journal*j,
882 OutputMode mode,
883 unsigned n_columns,
884 OutputFlags flags) = {
885
886 [OUTPUT_SHORT] = output_short,
887 [OUTPUT_SHORT_ISO] = output_short,
888 [OUTPUT_SHORT_PRECISE] = output_short,
889 [OUTPUT_SHORT_MONOTONIC] = output_short,
890 [OUTPUT_VERBOSE] = output_verbose,
891 [OUTPUT_EXPORT] = output_export,
892 [OUTPUT_JSON] = output_json,
893 [OUTPUT_JSON_PRETTY] = output_json,
894 [OUTPUT_JSON_SSE] = output_json,
895 [OUTPUT_CAT] = output_cat
896 };
897
898 int output_journal(
899 FILE *f,
900 sd_journal *j,
901 OutputMode mode,
902 unsigned n_columns,
903 OutputFlags flags,
904 bool *ellipsized) {
905
906 int ret;
907 assert(mode >= 0);
908 assert(mode < _OUTPUT_MODE_MAX);
909
910 if (n_columns <= 0)
911 n_columns = columns();
912
913 ret = output_funcs[mode](f, j, mode, n_columns, flags);
914 fflush(stdout);
915
916 if (ellipsized && ret > 0)
917 *ellipsized = true;
918
919 return ret;
920 }
921
922 static int show_journal(FILE *f,
923 sd_journal *j,
924 OutputMode mode,
925 unsigned n_columns,
926 usec_t not_before,
927 unsigned how_many,
928 OutputFlags flags,
929 bool *ellipsized) {
930
931 int r;
932 unsigned line = 0;
933 bool need_seek = false;
934 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
935
936 assert(j);
937 assert(mode >= 0);
938 assert(mode < _OUTPUT_MODE_MAX);
939
940 /* Seek to end */
941 r = sd_journal_seek_tail(j);
942 if (r < 0)
943 goto finish;
944
945 r = sd_journal_previous_skip(j, how_many);
946 if (r < 0)
947 goto finish;
948
949 for (;;) {
950 for (;;) {
951 usec_t usec;
952
953 if (need_seek) {
954 r = sd_journal_next(j);
955 if (r < 0)
956 goto finish;
957 }
958
959 if (r == 0)
960 break;
961
962 need_seek = true;
963
964 if (not_before > 0) {
965 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
966
967 /* -ESTALE is returned if the
968 timestamp is not from this boot */
969 if (r == -ESTALE)
970 continue;
971 else if (r < 0)
972 goto finish;
973
974 if (usec < not_before)
975 continue;
976 }
977
978 line ++;
979
980 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
981 if (r < 0)
982 goto finish;
983 }
984
985 if (warn_cutoff && line < how_many && not_before > 0) {
986 sd_id128_t boot_id;
987 usec_t cutoff;
988
989 /* Check whether the cutoff line is too early */
990
991 r = sd_id128_get_boot(&boot_id);
992 if (r < 0)
993 goto finish;
994
995 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
996 if (r < 0)
997 goto finish;
998
999 if (r > 0 && not_before < cutoff)
1000 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
1001
1002 warn_cutoff = false;
1003 }
1004
1005 if (!(flags & OUTPUT_FOLLOW))
1006 break;
1007
1008 r = sd_journal_wait(j, (usec_t) -1);
1009 if (r < 0)
1010 goto finish;
1011
1012 }
1013
1014 finish:
1015 return r;
1016 }
1017
1018 int add_matches_for_unit(sd_journal *j, const char *unit) {
1019 int r;
1020 char *m1, *m2, *m3, *m4;
1021
1022 assert(j);
1023 assert(unit);
1024
1025 m1 = strappenda("_SYSTEMD_UNIT=", unit);
1026 m2 = strappenda("COREDUMP_UNIT=", unit);
1027 m3 = strappenda("UNIT=", unit);
1028 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
1029
1030 (void)(
1031 /* Look for messages from the service itself */
1032 (r = sd_journal_add_match(j, m1, 0)) ||
1033
1034 /* Look for coredumps of the service */
1035 (r = sd_journal_add_disjunction(j)) ||
1036 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1037 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1038 (r = sd_journal_add_match(j, m2, 0)) ||
1039
1040 /* Look for messages from PID 1 about this service */
1041 (r = sd_journal_add_disjunction(j)) ||
1042 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1043 (r = sd_journal_add_match(j, m3, 0)) ||
1044
1045 /* Look for messages from authorized daemons about this service */
1046 (r = sd_journal_add_disjunction(j)) ||
1047 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1048 (r = sd_journal_add_match(j, m4, 0))
1049 );
1050
1051 if (r == 0 && endswith(unit, ".slice")) {
1052 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1053
1054 /* Show all messages belonging to a slice */
1055 (void)(
1056 (r = sd_journal_add_disjunction(j)) ||
1057 (r = sd_journal_add_match(j, m5, 0))
1058 );
1059 }
1060
1061 return r;
1062 }
1063
1064 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1065 int r;
1066 char *m1, *m2, *m3, *m4;
1067 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1068
1069 assert(j);
1070 assert(unit);
1071
1072 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1073 m2 = strappenda("USER_UNIT=", unit);
1074 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1075 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
1076 sprintf(muid, "_UID=%lu", (unsigned long) uid);
1077
1078 (void) (
1079 /* Look for messages from the user service itself */
1080 (r = sd_journal_add_match(j, m1, 0)) ||
1081 (r = sd_journal_add_match(j, muid, 0)) ||
1082
1083 /* Look for messages from systemd about this service */
1084 (r = sd_journal_add_disjunction(j)) ||
1085 (r = sd_journal_add_match(j, m2, 0)) ||
1086 (r = sd_journal_add_match(j, muid, 0)) ||
1087
1088 /* Look for coredumps of the service */
1089 (r = sd_journal_add_disjunction(j)) ||
1090 (r = sd_journal_add_match(j, m3, 0)) ||
1091 (r = sd_journal_add_match(j, muid, 0)) ||
1092 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1093
1094 /* Look for messages from authorized daemons about this service */
1095 (r = sd_journal_add_disjunction(j)) ||
1096 (r = sd_journal_add_match(j, m4, 0)) ||
1097 (r = sd_journal_add_match(j, muid, 0)) ||
1098 (r = sd_journal_add_match(j, "_UID=0", 0))
1099 );
1100
1101 if (r == 0 && endswith(unit, ".slice")) {
1102 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1103
1104 /* Show all messages belonging to a slice */
1105 (void)(
1106 (r = sd_journal_add_disjunction(j)) ||
1107 (r = sd_journal_add_match(j, m5, 0)) ||
1108 (r = sd_journal_add_match(j, muid, 0))
1109 );
1110 }
1111
1112 return r;
1113 }
1114
1115 int add_match_this_boot(sd_journal *j) {
1116 char match[9+32+1] = "_BOOT_ID=";
1117 sd_id128_t boot_id;
1118 int r;
1119
1120 assert(j);
1121
1122 r = sd_id128_get_boot(&boot_id);
1123 if (r < 0) {
1124 log_error("Failed to get boot id: %s", strerror(-r));
1125 return r;
1126 }
1127
1128 sd_id128_to_string(boot_id, match + 9);
1129 r = sd_journal_add_match(j, match, strlen(match));
1130 if (r < 0) {
1131 log_error("Failed to add match: %s", strerror(-r));
1132 return r;
1133 }
1134
1135 r = sd_journal_add_conjunction(j);
1136 if (r < 0)
1137 return r;
1138
1139 return 0;
1140 }
1141
1142 int show_journal_by_unit(
1143 FILE *f,
1144 const char *unit,
1145 OutputMode mode,
1146 unsigned n_columns,
1147 usec_t not_before,
1148 unsigned how_many,
1149 uid_t uid,
1150 OutputFlags flags,
1151 bool system,
1152 bool *ellipsized) {
1153
1154 _cleanup_journal_close_ sd_journal*j = NULL;
1155 int r;
1156 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1157
1158 assert(mode >= 0);
1159 assert(mode < _OUTPUT_MODE_MAX);
1160 assert(unit);
1161
1162 if (how_many <= 0)
1163 return 0;
1164
1165 r = sd_journal_open(&j, jflags);
1166 if (r < 0)
1167 return r;
1168
1169 r = add_match_this_boot(j);
1170 if (r < 0)
1171 return r;
1172
1173 if (system)
1174 r = add_matches_for_unit(j, unit);
1175 else
1176 r = add_matches_for_user_unit(j, unit, uid);
1177 if (r < 0)
1178 return r;
1179
1180 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1181 _cleanup_free_ char *filter;
1182
1183 filter = journal_make_match_string(j);
1184 log_debug("Journal filter: %s", filter);
1185 }
1186
1187 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1188 }
1189
1190 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1191 [OUTPUT_SHORT] = "short",
1192 [OUTPUT_SHORT_ISO] = "short-iso",
1193 [OUTPUT_SHORT_PRECISE] = "short-precise",
1194 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1195 [OUTPUT_VERBOSE] = "verbose",
1196 [OUTPUT_EXPORT] = "export",
1197 [OUTPUT_JSON] = "json",
1198 [OUTPUT_JSON_PRETTY] = "json-pretty",
1199 [OUTPUT_JSON_SSE] = "json-sse",
1200 [OUTPUT_CAT] = "cat"
1201 };
1202
1203 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);