1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2012 Lennart Poettering
12 #include <sys/socket.h>
18 #include "sd-journal.h"
20 #include "alloc-util.h"
22 #include "format-util.h"
24 #include "hostname-util.h"
26 #include "journal-internal.h"
28 #include "logs-show.h"
30 #include "output-mode.h"
31 #include "parse-util.h"
32 #include "process-util.h"
33 #include "sparse-endian.h"
34 #include "stdio-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
38 #include "terminal-util.h"
39 #include "time-util.h"
43 /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
44 #define PRINT_LINE_THRESHOLD 3
45 #define PRINT_CHAR_THRESHOLD 300
47 #define JSON_THRESHOLD 4096
49 static int print_catalog(FILE *f
, sd_journal
*j
) {
51 _cleanup_free_
char *t
= NULL
, *z
= NULL
;
53 r
= sd_journal_get_catalog(j
, &t
);
57 z
= strreplace(strstrip(t
), "\n", "\n-- ");
68 static int parse_field(const void *data
, size_t length
, const char *field
, size_t field_len
, char **target
, size_t *target_len
) {
76 if (length
< field_len
)
79 if (memcmp(data
, field
, field_len
))
82 nl
= length
- field_len
;
84 buf
= newdup_suffix0(char, (const char*) data
+ field_len
, nl
);
97 typedef struct ParseFieldVec
{
104 #define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) \
105 { .field = _field, .field_len = strlen(_field), .target = _target, .target_len = _target_len }
107 static int parse_fieldv(const void *data
, size_t length
, const ParseFieldVec
*fields
, unsigned n_fields
) {
110 for (i
= 0; i
< n_fields
; i
++) {
111 const ParseFieldVec
*f
= &fields
[i
];
114 r
= parse_field(data
, length
, f
->field
, f
->field_len
, f
->target
, f
->target_len
);
124 static int field_set_test(Set
*fields
, const char *name
, size_t n
) {
130 s
= strndupa(name
, n
);
134 return set_get(fields
, s
) ? 1 : 0;
137 static bool shall_print(const char *p
, size_t l
, OutputFlags flags
) {
140 if (flags
& OUTPUT_SHOW_ALL
)
143 if (l
>= PRINT_CHAR_THRESHOLD
)
146 if (!utf8_is_printable(p
, l
))
152 static bool print_multiline(
160 size_t highlight
[2]) {
162 const char *color_on
= "", *color_off
= "", *highlight_on
= "";
163 const char *pos
, *end
;
164 bool ellipsized
= false;
167 if (flags
& OUTPUT_COLOR
) {
168 if (priority
<= LOG_ERR
) {
169 color_on
= ANSI_HIGHLIGHT_RED
;
170 color_off
= ANSI_NORMAL
;
171 highlight_on
= ANSI_HIGHLIGHT
;
172 } else if (priority
<= LOG_NOTICE
) {
173 color_on
= ANSI_HIGHLIGHT
;
174 color_off
= ANSI_NORMAL
;
175 highlight_on
= ANSI_HIGHLIGHT_RED
;
179 /* A special case: make sure that we print a newline when
180 the message is empty. */
181 if (message_len
== 0)
185 pos
< message
+ message_len
;
186 pos
= end
+ 1, line
++) {
187 bool continuation
= line
> 0;
190 for (end
= pos
; end
< message
+ message_len
&& *end
!= '\n'; end
++)
195 /* We need to figure out when we are showing not-last line, *and*
196 * will skip subsequent lines. In that case, we will put the dots
197 * at the end of the line, instead of putting dots in the middle
201 line
+ 1 == PRINT_LINE_THRESHOLD
||
202 end
+ 1 >= message
+ PRINT_CHAR_THRESHOLD
;
204 if (flags
& (OUTPUT_FULL_WIDTH
| OUTPUT_SHOW_ALL
) ||
205 (prefix
+ len
+ 1 < n_columns
&& !tail_line
)) {
207 (size_t) (pos
- message
) <= highlight
[0] &&
208 highlight
[0] < (size_t) len
) {
210 fprintf(f
, "%*s%s%.*s",
211 continuation
* prefix
, "",
212 color_on
, (int) highlight
[0], pos
);
215 (int) (MIN((size_t) len
, highlight
[1]) - highlight
[0]),
217 if ((size_t) len
> highlight
[1])
220 (int) (len
- highlight
[1]),
222 fprintf(f
, "%s\n", color_off
);
225 fprintf(f
, "%*s%s%.*s%s\n",
226 continuation
* prefix
, "",
227 color_on
, len
, pos
, color_off
);
231 /* Beyond this point, ellipsization will happen. */
234 if (prefix
< n_columns
&& n_columns
- prefix
>= 3) {
235 if (n_columns
- prefix
> (unsigned) len
+ 3)
236 fprintf(f
, "%*s%s%.*s...%s\n",
237 continuation
* prefix
, "",
238 color_on
, len
, pos
, color_off
);
240 _cleanup_free_
char *e
;
242 e
= ellipsize_mem(pos
, len
, n_columns
- prefix
,
243 tail_line
? 100 : 90);
245 fprintf(f
, "%*s%s%.*s%s\n",
246 continuation
* prefix
, "",
247 color_on
, len
, pos
, color_off
);
249 fprintf(f
, "%*s%s%s%s\n",
250 continuation
* prefix
, "",
251 color_on
, e
, color_off
);
263 static int output_timestamp_monotonic(FILE *f
, sd_journal
*j
, const char *monotonic
) {
273 r
= safe_atou64(monotonic
, &t
);
275 r
= sd_journal_get_monotonic_usec(j
, &t
, &boot_id
);
277 return log_error_errno(r
, "Failed to get monotonic timestamp: %m");
279 fprintf(f
, "[%5"PRI_USEC
".%06"PRI_USEC
"]", t
/ USEC_PER_SEC
, t
% USEC_PER_SEC
);
280 return 1 + 5 + 1 + 6 + 1;
283 static int output_timestamp_realtime(FILE *f
, sd_journal
*j
, OutputMode mode
, OutputFlags flags
, const char *realtime
) {
284 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, 64)];
285 struct tm
*(*gettime_r
)(const time_t *, struct tm
*);
295 r
= safe_atou64(realtime
, &x
);
296 if (!realtime
|| r
< 0 || !VALID_REALTIME(x
))
297 r
= sd_journal_get_realtime_usec(j
, &x
);
299 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
301 if (IN_SET(mode
, OUTPUT_SHORT_FULL
, OUTPUT_WITH_UNIT
)) {
304 if (flags
& OUTPUT_UTC
)
305 k
= format_timestamp_utc(buf
, sizeof(buf
), x
);
307 k
= format_timestamp(buf
, sizeof(buf
), x
);
309 log_error("Failed to format timestamp: %"PRIu64
, x
);
316 gettime_r
= (flags
& OUTPUT_UTC
) ? gmtime_r
: localtime_r
;
317 t
= (time_t) (x
/ USEC_PER_SEC
);
321 case OUTPUT_SHORT_UNIX
:
322 xsprintf(buf
, "%10"PRI_TIME
".%06"PRIu64
, t
, x
% USEC_PER_SEC
);
325 case OUTPUT_SHORT_ISO
:
326 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t
, &tm
)) <= 0) {
327 log_error("Failed to format ISO time");
332 case OUTPUT_SHORT_ISO_PRECISE
:
333 /* No usec in strftime, so we leave space and copy over */
334 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t
, &tm
)) <= 0) {
335 log_error("Failed to format ISO-precise time");
338 xsprintf(usec
, "%06"PRI_USEC
, x
% USEC_PER_SEC
);
339 memcpy(buf
+ 20, usec
, 6);
343 case OUTPUT_SHORT_PRECISE
:
345 if (strftime(buf
, sizeof(buf
), "%b %d %H:%M:%S", gettime_r(&t
, &tm
)) <= 0) {
346 log_error("Failed to format syslog time");
350 if (mode
== OUTPUT_SHORT_PRECISE
) {
353 assert(sizeof(buf
) > strlen(buf
));
354 k
= sizeof(buf
) - strlen(buf
);
356 r
= snprintf(buf
+ strlen(buf
), k
, ".%06"PRIu64
, x
% USEC_PER_SEC
);
357 if (r
<= 0 || (size_t) r
>= k
) { /* too long? */
358 log_error("Failed to format precise time");
365 assert_not_reached("Unknown time format");
370 return (int) strlen(buf
);
373 static int output_short(
380 size_t highlight
[2]) {
386 _cleanup_free_
char *hostname
= NULL
, *identifier
= NULL
, *comm
= NULL
, *pid
= NULL
, *fake_pid
= NULL
, *message
= NULL
, *realtime
= NULL
, *monotonic
= NULL
, *priority
= NULL
, *unit
= NULL
, *user_unit
= NULL
;
387 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, unit_len
= 0, user_unit_len
= 0;
389 bool ellipsized
= false;
390 const ParseFieldVec fields
[] = {
391 PARSE_FIELD_VEC_ENTRY("_PID=", &pid
, &pid_len
),
392 PARSE_FIELD_VEC_ENTRY("_COMM=", &comm
, &comm_len
),
393 PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message
, &message_len
),
394 PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority
, &priority_len
),
395 PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname
, &hostname_len
),
396 PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid
, &fake_pid_len
),
397 PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier
, &identifier_len
),
398 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime
, &realtime_len
),
399 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic
, &monotonic_len
),
400 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit
, &unit_len
),
401 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit
, &user_unit_len
),
403 size_t highlight_shifted
[] = {highlight
? highlight
[0] : 0, highlight
? highlight
[1] : 0};
408 /* Set the threshold to one bigger than the actual print
409 * threshold, so that if the line is actually longer than what
410 * we're willing to print, ellipsization will occur. This way
411 * we won't output a misleading line without any indication of
414 sd_journal_set_data_threshold(j
, flags
& (OUTPUT_SHOW_ALL
|OUTPUT_FULL_WIDTH
) ? 0 : PRINT_CHAR_THRESHOLD
+ 1);
416 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
417 r
= parse_fieldv(data
, length
, fields
, ELEMENTSOF(fields
));
422 log_debug_errno(r
, "Skipping message we can't read: %m");
426 return log_error_errno(r
, "Failed to get journal fields: %m");
429 log_debug("Skipping message without MESSAGE= field.");
433 if (!(flags
& OUTPUT_SHOW_ALL
))
434 strip_tab_ansi(&message
, &message_len
, highlight_shifted
);
436 if (priority_len
== 1 && *priority
>= '0' && *priority
<= '7')
439 if (mode
== OUTPUT_SHORT_MONOTONIC
)
440 r
= output_timestamp_monotonic(f
, j
, monotonic
);
442 r
= output_timestamp_realtime(f
, j
, mode
, flags
, realtime
);
447 if (flags
& OUTPUT_NO_HOSTNAME
) {
448 /* Suppress display of the hostname if this is requested. */
449 hostname
= mfree(hostname
);
453 if (hostname
&& shall_print(hostname
, hostname_len
, flags
)) {
454 fprintf(f
, " %.*s", (int) hostname_len
, hostname
);
455 n
+= hostname_len
+ 1;
458 if (mode
== OUTPUT_WITH_UNIT
&& ((unit
&& shall_print(unit
, unit_len
, flags
)) || (user_unit
&& shall_print(user_unit
, user_unit_len
, flags
)))) {
460 fprintf(f
, " %.*s", (int) unit_len
, unit
);
465 fprintf(f
, "/%.*s", (int) user_unit_len
, user_unit
);
467 fprintf(f
, " %.*s", (int) user_unit_len
, user_unit
);
470 } else if (identifier
&& shall_print(identifier
, identifier_len
, flags
)) {
471 fprintf(f
, " %.*s", (int) identifier_len
, identifier
);
472 n
+= identifier_len
+ 1;
473 } else if (comm
&& shall_print(comm
, comm_len
, flags
)) {
474 fprintf(f
, " %.*s", (int) comm_len
, comm
);
477 fputs(" unknown", f
);
479 if (pid
&& shall_print(pid
, pid_len
, flags
)) {
480 fprintf(f
, "[%.*s]", (int) pid_len
, pid
);
482 } else if (fake_pid
&& shall_print(fake_pid
, fake_pid_len
, flags
)) {
483 fprintf(f
, "[%.*s]", (int) fake_pid_len
, fake_pid
);
484 n
+= fake_pid_len
+ 2;
487 if (!(flags
& OUTPUT_SHOW_ALL
) && !utf8_is_printable(message
, message_len
)) {
488 char bytes
[FORMAT_BYTES_MAX
];
489 fprintf(f
, ": [%s blob data]\n", format_bytes(bytes
, sizeof(bytes
), message_len
));
493 print_multiline(f
, n
+ 2, n_columns
, flags
, p
,
494 message
, message_len
,
498 if (flags
& OUTPUT_CATALOG
)
504 static int output_verbose(
511 size_t highlight
[2]) {
515 _cleanup_free_
char *cursor
= NULL
;
516 uint64_t realtime
= 0;
517 char ts
[FORMAT_TIMESTAMP_MAX
+ 7];
518 const char *timestamp
;
524 sd_journal_set_data_threshold(j
, 0);
526 r
= sd_journal_get_data(j
, "_SOURCE_REALTIME_TIMESTAMP", &data
, &length
);
528 log_debug("Source realtime timestamp not found");
530 return log_full_errno(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_ERR
, r
, "Failed to get source realtime timestamp: %m");
532 _cleanup_free_
char *value
= NULL
;
534 r
= parse_field(data
, length
, "_SOURCE_REALTIME_TIMESTAMP=",
535 STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value
,
541 r
= safe_atou64(value
, &realtime
);
543 log_debug_errno(r
, "Failed to parse realtime timestamp: %m");
547 r
= sd_journal_get_realtime_usec(j
, &realtime
);
549 return log_full_errno(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_ERR
, r
, "Failed to get realtime timestamp: %m");
552 r
= sd_journal_get_cursor(j
, &cursor
);
554 return log_error_errno(r
, "Failed to get cursor: %m");
556 timestamp
= flags
& OUTPUT_UTC
? format_timestamp_us_utc(ts
, sizeof ts
, realtime
)
557 : format_timestamp_us(ts
, sizeof ts
, realtime
);
558 fprintf(f
, "%s [%s]\n",
559 timestamp
?: "(no timestamp)",
562 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
565 const char *on
= "", *off
= "";
567 c
= memchr(data
, '=', length
);
569 log_error("Invalid field.");
572 fieldlen
= c
- (const char*) data
;
574 r
= field_set_test(output_fields
, data
, fieldlen
);
580 if (flags
& OUTPUT_COLOR
&& startswith(data
, "MESSAGE=")) {
585 if ((flags
& OUTPUT_SHOW_ALL
) ||
586 (((length
< PRINT_CHAR_THRESHOLD
) || flags
& OUTPUT_FULL_WIDTH
)
587 && utf8_is_printable(data
, length
))) {
588 fprintf(f
, " %s%.*s=", on
, fieldlen
, (const char*)data
);
589 print_multiline(f
, 4 + fieldlen
+ 1, 0, OUTPUT_FULL_WIDTH
, 0, c
+ 1, length
- fieldlen
- 1, NULL
);
592 char bytes
[FORMAT_BYTES_MAX
];
594 fprintf(f
, " %s%.*s=[%s blob data]%s\n",
596 (int) (c
- (const char*) data
),
598 format_bytes(bytes
, sizeof(bytes
), length
- (c
- (const char *) data
) - 1),
606 if (flags
& OUTPUT_CATALOG
)
612 static int output_export(
619 size_t highlight
[2]) {
624 usec_t realtime
, monotonic
;
625 _cleanup_free_
char *cursor
= NULL
;
631 sd_journal_set_data_threshold(j
, 0);
633 r
= sd_journal_get_realtime_usec(j
, &realtime
);
635 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
637 r
= sd_journal_get_monotonic_usec(j
, &monotonic
, &boot_id
);
639 return log_error_errno(r
, "Failed to get monotonic timestamp: %m");
641 r
= sd_journal_get_cursor(j
, &cursor
);
643 return log_error_errno(r
, "Failed to get cursor: %m");
647 "__REALTIME_TIMESTAMP="USEC_FMT
"\n"
648 "__MONOTONIC_TIMESTAMP="USEC_FMT
"\n"
653 sd_id128_to_string(boot_id
, sid
));
655 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
658 /* We already printed the boot id from the data in the header, hence let's suppress it here */
659 if (memory_startswith(data
, length
, "_BOOT_ID="))
662 c
= memchr(data
, '=', length
);
664 log_error("Invalid field.");
668 r
= field_set_test(output_fields
, data
, c
- (const char *) data
);
674 if (utf8_is_printable_newline(data
, length
, false))
675 fwrite(data
, length
, 1, f
);
679 fwrite(data
, c
- (const char*) data
, 1, f
);
681 le64
= htole64(length
- (c
- (const char*) data
) - 1);
682 fwrite(&le64
, sizeof(le64
), 1, f
);
683 fwrite(c
+ 1, length
- (c
- (const char*) data
) - 1, 1, f
);
689 log_debug_errno(r
, "Skipping message we can't read: %m");
710 if (!(flags
& OUTPUT_SHOW_ALL
) && l
>= JSON_THRESHOLD
)
713 else if (!(flags
& OUTPUT_SHOW_ALL
) && !utf8_is_printable(p
, l
)) {
714 bool not_first
= false;
720 fprintf(f
, ", %u", (uint8_t) *p
);
723 fprintf(f
, "%u", (uint8_t) *p
);
735 if (IN_SET(*p
, '"', '\\')) {
738 } else if (*p
== '\n')
740 else if ((uint8_t) *p
< ' ')
741 fprintf(f
, "\\u%04x", (uint8_t) *p
);
753 static int output_json(
760 size_t highlight
[2]) {
762 uint64_t realtime
, monotonic
;
763 _cleanup_free_
char *cursor
= NULL
;
770 bool done
, separator
;
774 sd_journal_set_data_threshold(j
, flags
& OUTPUT_SHOW_ALL
? 0 : JSON_THRESHOLD
);
776 r
= sd_journal_get_realtime_usec(j
, &realtime
);
778 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
780 r
= sd_journal_get_monotonic_usec(j
, &monotonic
, &boot_id
);
782 return log_error_errno(r
, "Failed to get monotonic timestamp: %m");
784 r
= sd_journal_get_cursor(j
, &cursor
);
786 return log_error_errno(r
, "Failed to get cursor: %m");
788 if (mode
== OUTPUT_JSON_PRETTY
)
791 "\t\"__CURSOR\" : \"%s\",\n"
792 "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT
"\",\n"
793 "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT
"\",\n"
794 "\t\"_BOOT_ID\" : \"%s\"",
798 sd_id128_to_string(boot_id
, sid
));
800 if (mode
== OUTPUT_JSON_SSE
)
804 "{ \"__CURSOR\" : \"%s\", "
805 "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT
"\", "
806 "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT
"\", "
807 "\"_BOOT_ID\" : \"%s\"",
811 sd_id128_to_string(boot_id
, sid
));
814 h
= hashmap_new(&string_hash_ops
);
818 /* First round, iterate through the entry and count how often each field appears */
819 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
824 if (memory_startswith(data
, length
, "_BOOT_ID="))
827 eq
= memchr(data
, '=', length
);
831 n
= memdup_suffix0(data
, eq
- (const char*) data
);
837 u
= PTR_TO_UINT(hashmap_get(h
, n
));
839 r
= hashmap_put(h
, n
, UINT_TO_PTR(1));
846 r
= hashmap_update(h
, n
, UINT_TO_PTR(u
+ 1));
855 log_debug_errno(r
, "Skipping message we can't read: %m");
865 SD_JOURNAL_FOREACH_DATA(j
, data
, length
) {
868 _cleanup_free_
char *n
= NULL
;
872 /* We already printed the boot id from the data in
873 * the header, hence let's suppress it here */
874 if (memory_startswith(data
, length
, "_BOOT_ID="))
877 eq
= memchr(data
, '=', length
);
881 m
= eq
- (const char*) data
;
882 n
= memdup_suffix0(data
, m
);
888 if (output_fields
&& !set_get(output_fields
, n
))
892 fputs(mode
== OUTPUT_JSON_PRETTY
? ",\n\t" : ", ", f
);
894 u
= PTR_TO_UINT(hashmap_get2(h
, n
, (void**) &kk
));
896 /* We already printed this, let's jump to the next */
900 /* Field only appears once, output it directly */
902 json_escape(f
, data
, m
, flags
);
905 json_escape(f
, eq
+ 1, length
- m
- 1, flags
);
907 hashmap_remove(h
, n
);
913 /* Field appears multiple times, output it as array */
914 json_escape(f
, data
, m
, flags
);
916 json_escape(f
, eq
+ 1, length
- m
- 1, flags
);
918 /* Iterate through the end of the list */
920 while (sd_journal_enumerate_data(j
, &data
, &length
) > 0) {
924 if (memcmp(data
, n
, m
) != 0)
927 if (((const char*) data
)[m
] != '=')
931 json_escape(f
, (const char*) data
+ m
+ 1, length
- m
- 1, flags
);
936 hashmap_remove(h
, n
);
939 /* Iterate data fields form the beginning */
949 if (mode
== OUTPUT_JSON_PRETTY
)
951 else if (mode
== OUTPUT_JSON_SSE
)
959 while ((k
= hashmap_steal_first_key(h
)))
967 static int output_cat(
974 size_t highlight
[2]) {
979 const char *highlight_on
= "", *highlight_off
= "";
984 if (flags
& OUTPUT_COLOR
) {
985 highlight_on
= ANSI_HIGHLIGHT_RED
;
986 highlight_off
= ANSI_NORMAL
;
989 sd_journal_set_data_threshold(j
, 0);
991 r
= sd_journal_get_data(j
, "MESSAGE", &data
, &l
);
993 log_debug_errno(r
, "Skipping message we can't read: %m");
997 /* An entry without MESSAGE=? */
1001 return log_error_errno(r
, "Failed to get data: %m");
1006 if (highlight
&& (flags
& OUTPUT_COLOR
)) {
1007 assert(highlight
[0] <= highlight
[1]);
1008 assert(highlight
[1] <= l
- 8);
1010 fwrite((const char*) data
+ 8, 1, highlight
[0], f
);
1011 fwrite(highlight_on
, 1, strlen(highlight_on
), f
);
1012 fwrite((const char*) data
+ 8 + highlight
[0], 1, highlight
[1] - highlight
[0], f
);
1013 fwrite(highlight_off
, 1, strlen(highlight_off
), f
);
1014 fwrite((const char*) data
+ 8 + highlight
[1], 1, l
- 8 - highlight
[1], f
);
1016 fwrite((const char*) data
+ 8, 1, l
- 8, f
);
1022 static int (*output_funcs
[_OUTPUT_MODE_MAX
])(
1029 size_t highlight
[2]) = {
1031 [OUTPUT_SHORT
] = output_short
,
1032 [OUTPUT_SHORT_ISO
] = output_short
,
1033 [OUTPUT_SHORT_ISO_PRECISE
] = output_short
,
1034 [OUTPUT_SHORT_PRECISE
] = output_short
,
1035 [OUTPUT_SHORT_MONOTONIC
] = output_short
,
1036 [OUTPUT_SHORT_UNIX
] = output_short
,
1037 [OUTPUT_SHORT_FULL
] = output_short
,
1038 [OUTPUT_VERBOSE
] = output_verbose
,
1039 [OUTPUT_EXPORT
] = output_export
,
1040 [OUTPUT_JSON
] = output_json
,
1041 [OUTPUT_JSON_PRETTY
] = output_json
,
1042 [OUTPUT_JSON_SSE
] = output_json
,
1043 [OUTPUT_CAT
] = output_cat
,
1044 [OUTPUT_WITH_UNIT
] = output_short
,
1047 int show_journal_entry(
1053 char **output_fields
,
1054 size_t highlight
[2],
1058 _cleanup_set_free_free_ Set
*fields
= NULL
;
1060 assert(mode
< _OUTPUT_MODE_MAX
);
1063 n_columns
= columns();
1065 if (output_fields
) {
1066 fields
= set_new(&string_hash_ops
);
1070 ret
= set_put_strdupv(fields
, output_fields
);
1075 ret
= output_funcs
[mode
](f
, j
, mode
, n_columns
, flags
, fields
, highlight
);
1077 if (ellipsized
&& ret
> 0)
1083 static int maybe_print_begin_newline(FILE *f
, OutputFlags
*flags
) {
1087 if (!(*flags
& OUTPUT_BEGIN_NEWLINE
))
1090 /* Print a beginning new line if that's request, but only once
1091 * on the first line we print. */
1094 *flags
&= ~OUTPUT_BEGIN_NEWLINE
;
1110 bool need_seek
= false;
1111 int warn_cutoff
= flags
& OUTPUT_WARN_CUTOFF
;
1115 assert(mode
< _OUTPUT_MODE_MAX
);
1117 if (how_many
== (unsigned) -1)
1121 r
= sd_journal_seek_tail(j
);
1123 return log_error_errno(r
, "Failed to seek to tail: %m");
1125 r
= sd_journal_previous_skip(j
, how_many
);
1127 return log_error_errno(r
, "Failed to skip previous: %m");
1135 r
= sd_journal_next(j
);
1137 return log_error_errno(r
, "Failed to iterate through journal: %m");
1145 if (not_before
> 0) {
1146 r
= sd_journal_get_monotonic_usec(j
, &usec
, NULL
);
1148 /* -ESTALE is returned if the
1149 timestamp is not from this boot */
1153 return log_error_errno(r
, "Failed to get journal time: %m");
1155 if (usec
< not_before
)
1160 maybe_print_begin_newline(f
, &flags
);
1162 r
= show_journal_entry(f
, j
, mode
, n_columns
, flags
, NULL
, NULL
, ellipsized
);
1167 if (warn_cutoff
&& line
< how_many
&& not_before
> 0) {
1171 /* Check whether the cutoff line is too early */
1173 r
= sd_id128_get_boot(&boot_id
);
1175 return log_error_errno(r
, "Failed to get boot id: %m");
1177 r
= sd_journal_get_cutoff_monotonic_usec(j
, boot_id
, &cutoff
, NULL
);
1179 return log_error_errno(r
, "Failed to get journal cutoff time: %m");
1181 if (r
> 0 && not_before
< cutoff
) {
1182 maybe_print_begin_newline(f
, &flags
);
1183 fprintf(f
, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
1186 warn_cutoff
= false;
1189 if (!(flags
& OUTPUT_FOLLOW
))
1192 r
= sd_journal_wait(j
, USEC_INFINITY
);
1194 return log_error_errno(r
, "Failed to wait for journal: %m");
1201 int add_matches_for_unit(sd_journal
*j
, const char *unit
) {
1202 const char *m1
, *m2
, *m3
, *m4
;
1208 m1
= strjoina("_SYSTEMD_UNIT=", unit
);
1209 m2
= strjoina("COREDUMP_UNIT=", unit
);
1210 m3
= strjoina("UNIT=", unit
);
1211 m4
= strjoina("OBJECT_SYSTEMD_UNIT=", unit
);
1214 /* Look for messages from the service itself */
1215 (r
= sd_journal_add_match(j
, m1
, 0)) ||
1217 /* Look for coredumps of the service */
1218 (r
= sd_journal_add_disjunction(j
)) ||
1219 (r
= sd_journal_add_match(j
, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1220 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1221 (r
= sd_journal_add_match(j
, m2
, 0)) ||
1223 /* Look for messages from PID 1 about this service */
1224 (r
= sd_journal_add_disjunction(j
)) ||
1225 (r
= sd_journal_add_match(j
, "_PID=1", 0)) ||
1226 (r
= sd_journal_add_match(j
, m3
, 0)) ||
1228 /* Look for messages from authorized daemons about this service */
1229 (r
= sd_journal_add_disjunction(j
)) ||
1230 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1231 (r
= sd_journal_add_match(j
, m4
, 0))
1234 if (r
== 0 && endswith(unit
, ".slice")) {
1237 m5
= strjoina("_SYSTEMD_SLICE=", unit
);
1239 /* Show all messages belonging to a slice */
1241 (r
= sd_journal_add_disjunction(j
)) ||
1242 (r
= sd_journal_add_match(j
, m5
, 0))
1249 int add_matches_for_user_unit(sd_journal
*j
, const char *unit
, uid_t uid
) {
1251 char *m1
, *m2
, *m3
, *m4
;
1252 char muid
[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t
)];
1257 m1
= strjoina("_SYSTEMD_USER_UNIT=", unit
);
1258 m2
= strjoina("USER_UNIT=", unit
);
1259 m3
= strjoina("COREDUMP_USER_UNIT=", unit
);
1260 m4
= strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit
);
1261 sprintf(muid
, "_UID="UID_FMT
, uid
);
1264 /* Look for messages from the user service itself */
1265 (r
= sd_journal_add_match(j
, m1
, 0)) ||
1266 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1268 /* Look for messages from systemd about this service */
1269 (r
= sd_journal_add_disjunction(j
)) ||
1270 (r
= sd_journal_add_match(j
, m2
, 0)) ||
1271 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1273 /* Look for coredumps of the service */
1274 (r
= sd_journal_add_disjunction(j
)) ||
1275 (r
= sd_journal_add_match(j
, m3
, 0)) ||
1276 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1277 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1279 /* Look for messages from authorized daemons about this service */
1280 (r
= sd_journal_add_disjunction(j
)) ||
1281 (r
= sd_journal_add_match(j
, m4
, 0)) ||
1282 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1283 (r
= sd_journal_add_match(j
, "_UID=0", 0))
1286 if (r
== 0 && endswith(unit
, ".slice")) {
1289 m5
= strjoina("_SYSTEMD_SLICE=", unit
);
1291 /* Show all messages belonging to a slice */
1293 (r
= sd_journal_add_disjunction(j
)) ||
1294 (r
= sd_journal_add_match(j
, m5
, 0)) ||
1295 (r
= sd_journal_add_match(j
, muid
, 0))
1302 static int get_boot_id_for_machine(const char *machine
, sd_id128_t
*boot_id
) {
1303 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1304 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, rootfd
= -1;
1313 if (!machine_name_is_valid(machine
))
1316 r
= container_get_leader(machine
, &pid
);
1320 r
= namespace_open(pid
, &pidnsfd
, &mntnsfd
, NULL
, NULL
, &rootfd
);
1324 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1327 r
= safe_fork("(sd-bootid)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
1333 pair
[0] = safe_close(pair
[0]);
1335 r
= namespace_enter(pidnsfd
, mntnsfd
, -1, -1, rootfd
);
1337 _exit(EXIT_FAILURE
);
1339 fd
= open("/proc/sys/kernel/random/boot_id", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1341 _exit(EXIT_FAILURE
);
1343 r
= loop_read_exact(fd
, buf
, 36, false);
1346 _exit(EXIT_FAILURE
);
1348 k
= send(pair
[1], buf
, 36, MSG_NOSIGNAL
);
1350 _exit(EXIT_FAILURE
);
1352 _exit(EXIT_SUCCESS
);
1355 pair
[1] = safe_close(pair
[1]);
1357 r
= wait_for_terminate_and_check("(sd-bootid)", child
, 0);
1360 if (r
!= EXIT_SUCCESS
)
1363 k
= recv(pair
[0], buf
, 36, 0);
1368 r
= sd_id128_from_string(buf
, boot_id
);
1375 int add_match_this_boot(sd_journal
*j
, const char *machine
) {
1376 char match
[9+32+1] = "_BOOT_ID=";
1383 r
= get_boot_id_for_machine(machine
, &boot_id
);
1385 return log_error_errno(r
, "Failed to get boot id of container %s: %m", machine
);
1387 r
= sd_id128_get_boot(&boot_id
);
1389 return log_error_errno(r
, "Failed to get boot id: %m");
1392 sd_id128_to_string(boot_id
, match
+ 9);
1393 r
= sd_journal_add_match(j
, match
, strlen(match
));
1395 return log_error_errno(r
, "Failed to add match: %m");
1397 r
= sd_journal_add_conjunction(j
);
1399 return log_error_errno(r
, "Failed to add conjunction: %m");
1404 int show_journal_by_unit(
1413 int journal_open_flags
,
1417 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1421 assert(mode
< _OUTPUT_MODE_MAX
);
1427 r
= sd_journal_open(&j
, journal_open_flags
);
1429 return log_error_errno(r
, "Failed to open journal: %m");
1431 r
= add_match_this_boot(j
, NULL
);
1436 r
= add_matches_for_unit(j
, unit
);
1438 r
= add_matches_for_user_unit(j
, unit
, uid
);
1440 return log_error_errno(r
, "Failed to add unit matches: %m");
1442 if (DEBUG_LOGGING
) {
1443 _cleanup_free_
char *filter
;
1445 filter
= journal_make_match_string(j
);
1449 log_debug("Journal filter: %s", filter
);
1452 return show_journal(f
, j
, mode
, n_columns
, not_before
, how_many
, flags
, ellipsized
);