1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include <sys/socket.h>
13 #include "sd-journal.h"
15 #include "alloc-util.h"
17 #include "format-util.h"
19 #include "hostname-util.h"
20 #include "id128-util.h"
22 #include "journal-internal.h"
23 #include "journal-util.h"
25 #include "locale-util.h"
27 #include "logs-show.h"
29 #include "namespace-util.h"
30 #include "output-mode.h"
31 #include "parse-util.h"
32 #include "pretty-print.h"
33 #include "process-util.h"
34 #include "sparse-endian.h"
35 #include "stdio-util.h"
36 #include "string-table.h"
37 #include "string-util.h"
39 #include "terminal-util.h"
40 #include "time-util.h"
45 /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
46 #define PRINT_LINE_THRESHOLD 3
47 #define PRINT_CHAR_THRESHOLD 300
49 #define JSON_THRESHOLD 4096U
51 static int print_catalog(FILE *f
, sd_journal
*j
) {
52 _cleanup_free_
char *t
= NULL
, *z
= NULL
;
53 const char *newline
, *prefix
;
58 r
= sd_journal_get_catalog(j
, &t
);
62 return log_error_errno(r
, "Failed to find catalog entry: %m");
65 prefix
= strjoina(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE
), special_glyph(SPECIAL_GLYPH_LIGHT_SHADE
));
70 newline
= strjoina(ANSI_NORMAL
"\n" ANSI_GREY
, prefix
, ANSI_NORMAL
" " ANSI_GREEN
);
72 newline
= strjoina("\n", prefix
, " ");
74 z
= strreplace(strstrip(t
), "\n", newline
);
79 fprintf(f
, ANSI_GREY
"%s" ANSI_NORMAL
" " ANSI_GREEN
, prefix
);
81 fprintf(f
, "%s ", prefix
);
86 fputs(ANSI_NORMAL
"\n", f
);
93 static int url_from_catalog(sd_journal
*j
, char **ret
) {
94 _cleanup_free_
char *t
= NULL
, *url
= NULL
;
101 r
= sd_journal_get_catalog(j
, &t
);
105 return log_error_errno(r
, "Failed to find catalog entry: %m");
107 weblink
= startswith(t
, "Documentation:");
109 weblink
= strstr(t
+ 1, "\nDocumentation:");
116 /* Skip whitespace to value */
117 weblink
+= strspn(weblink
, " \t");
119 /* Cut out till next whitespace/newline */
120 url
= strndup(weblink
, strcspn(weblink
, WHITESPACE
));
124 if (!documentation_url_is_valid(url
))
127 *ret
= TAKE_PTR(url
);
135 static int parse_field(const void *data
, size_t length
, const char *field
, size_t field_len
, char **target
, size_t *target_len
) {
143 if (length
< field_len
)
146 if (memcmp(data
, field
, field_len
))
149 nl
= length
- field_len
;
151 buf
= newdup_suffix0(char, (const char*) data
+ field_len
, nl
);
164 typedef struct ParseFieldVec
{
171 #define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) { \
173 .field_len = strlen(_field), \
175 .target_len = _target_len \
178 static int parse_fieldv(const void *data
, size_t length
, const ParseFieldVec
*fields
, unsigned n_fields
) {
181 for (i
= 0; i
< n_fields
; i
++) {
182 const ParseFieldVec
*f
= &fields
[i
];
185 r
= parse_field(data
, length
, f
->field
, f
->field_len
, f
->target
, f
->target_len
);
195 static int field_set_test(const Set
*fields
, const char *name
, size_t n
) {
201 s
= strndupa(name
, n
);
202 return set_contains(fields
, s
);
205 static bool shall_print(const char *p
, size_t l
, OutputFlags flags
) {
208 if (flags
& OUTPUT_SHOW_ALL
)
211 if (l
>= PRINT_CHAR_THRESHOLD
)
214 if (!utf8_is_printable(p
, l
))
220 static bool print_multiline(
229 size_t highlight
[2]) {
231 const char *color_on
= "", *color_off
= "", *highlight_on
= "";
232 const char *pos
, *end
;
233 bool ellipsized
= false;
236 if (flags
& OUTPUT_COLOR
) {
237 get_log_colors(priority
, &color_on
, &color_off
, &highlight_on
);
239 if (audit
&& strempty(color_on
)) {
240 color_on
= ANSI_BLUE
;
241 color_off
= ANSI_NORMAL
;
245 /* A special case: make sure that we print a newline when
246 the message is empty. */
247 if (message_len
== 0)
251 pos
< message
+ message_len
;
252 pos
= end
+ 1, line
++) {
253 bool continuation
= line
> 0;
256 for (end
= pos
; end
< message
+ message_len
&& *end
!= '\n'; end
++)
261 /* We need to figure out when we are showing not-last line, *and*
262 * will skip subsequent lines. In that case, we will put the dots
263 * at the end of the line, instead of putting dots in the middle
267 line
+ 1 == PRINT_LINE_THRESHOLD
||
268 end
+ 1 >= message
+ PRINT_CHAR_THRESHOLD
;
270 if (flags
& (OUTPUT_FULL_WIDTH
| OUTPUT_SHOW_ALL
) ||
271 (prefix
+ len
+ 1 < n_columns
&& !tail_line
)) {
273 (size_t) (pos
- message
) <= highlight
[0] &&
274 highlight
[0] < (size_t) len
) {
276 fprintf(f
, "%*s%s%.*s",
277 continuation
* prefix
, "",
278 color_on
, (int) highlight
[0], pos
);
281 (int) (MIN((size_t) len
, highlight
[1]) - highlight
[0]),
283 if ((size_t) len
> highlight
[1])
286 (int) (len
- highlight
[1]),
288 fprintf(f
, "%s\n", color_off
);
291 fprintf(f
, "%*s%s%.*s%s\n",
292 continuation
* prefix
, "",
293 color_on
, len
, pos
, color_off
);
297 /* Beyond this point, ellipsization will happen. */
300 if (prefix
< n_columns
&& n_columns
- prefix
>= 3) {
301 if (n_columns
- prefix
> (unsigned) len
+ 3)
302 fprintf(f
, "%*s%s%.*s...%s\n",
303 continuation
* prefix
, "",
304 color_on
, len
, pos
, color_off
);
306 _cleanup_free_
char *e
;
308 e
= ellipsize_mem(pos
, len
, n_columns
- prefix
,
309 tail_line
? 100 : 90);
311 fprintf(f
, "%*s%s%.*s%s\n",
312 continuation
* prefix
, "",
313 color_on
, len
, pos
, color_off
);
315 fprintf(f
, "%*s%s%s%s\n",
316 continuation
* prefix
, "",
317 color_on
, e
, color_off
);
329 static int output_timestamp_monotonic(FILE *f
, sd_journal
*j
, const char *monotonic
) {
339 r
= safe_atou64(monotonic
, &t
);
341 r
= sd_journal_get_monotonic_usec(j
, &t
, &boot_id
);
343 return log_error_errno(r
, "Failed to get monotonic timestamp: %m");
345 fprintf(f
, "[%5"PRI_USEC
".%06"PRI_USEC
"]", t
/ USEC_PER_SEC
, t
% USEC_PER_SEC
);
346 return 1 + 5 + 1 + 6 + 1;
349 static int output_timestamp_realtime(FILE *f
, sd_journal
*j
, OutputMode mode
, OutputFlags flags
, const char *realtime
) {
350 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, 64)];
351 struct tm
*(*gettime_r
)(const time_t *, struct tm
*);
361 r
= safe_atou64(realtime
, &x
);
362 if (!realtime
|| r
< 0 || !VALID_REALTIME(x
))
363 r
= sd_journal_get_realtime_usec(j
, &x
);
365 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
367 if (IN_SET(mode
, OUTPUT_SHORT_FULL
, OUTPUT_WITH_UNIT
)) {
370 if (flags
& OUTPUT_UTC
)
371 k
= format_timestamp_utc(buf
, sizeof(buf
), x
);
373 k
= format_timestamp(buf
, sizeof(buf
), x
);
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
376 "Failed to format timestamp: %" PRIu64
, x
);
381 gettime_r
= (flags
& OUTPUT_UTC
) ? gmtime_r
: localtime_r
;
382 t
= (time_t) (x
/ USEC_PER_SEC
);
386 case OUTPUT_SHORT_UNIX
:
387 xsprintf(buf
, "%10"PRI_TIME
".%06"PRIu64
, t
, x
% USEC_PER_SEC
);
390 case OUTPUT_SHORT_ISO
:
391 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t
, &tm
)) <= 0)
392 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
393 "Failed to format ISO time");
396 case OUTPUT_SHORT_ISO_PRECISE
:
397 /* No usec in strftime, so we leave space and copy over */
398 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t
, &tm
)) <= 0)
399 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
400 "Failed to format ISO-precise time");
401 xsprintf(usec
, "%06"PRI_USEC
, x
% USEC_PER_SEC
);
402 memcpy(buf
+ 20, usec
, 6);
406 case OUTPUT_SHORT_PRECISE
:
408 if (strftime(buf
, sizeof(buf
), "%b %d %H:%M:%S", gettime_r(&t
, &tm
)) <= 0)
409 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
410 "Failed to format syslog time");
412 if (mode
== OUTPUT_SHORT_PRECISE
) {
415 assert(sizeof(buf
) > strlen(buf
));
416 k
= sizeof(buf
) - strlen(buf
);
418 r
= snprintf(buf
+ strlen(buf
), k
, ".%06"PRIu64
, x
% USEC_PER_SEC
);
419 if (r
<= 0 || (size_t) r
>= k
) /* too long? */
420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
421 "Failed to format precise time");
426 assert_not_reached("Unknown time format");
431 return (int) strlen(buf
);
434 static int output_short(
440 const Set
*output_fields
,
441 const size_t highlight
[2]) {
445 size_t length
, n
= 0;
446 _cleanup_free_
char *hostname
= NULL
, *identifier
= NULL
, *comm
= NULL
, *pid
= NULL
, *fake_pid
= NULL
,
447 *message
= NULL
, *realtime
= NULL
, *monotonic
= NULL
, *priority
= NULL
, *transport
= NULL
,
448 *config_file
= NULL
, *unit
= NULL
, *user_unit
= NULL
, *documentation_url
= NULL
;
449 size_t hostname_len
= 0, identifier_len
= 0, comm_len
= 0, pid_len
= 0, fake_pid_len
= 0, message_len
= 0,
450 realtime_len
= 0, monotonic_len
= 0, priority_len
= 0, transport_len
= 0, config_file_len
= 0,
451 unit_len
= 0, user_unit_len
= 0, documentation_url_len
= 0;
453 bool ellipsized
= false, audit
;
454 const ParseFieldVec fields
[] = {
455 PARSE_FIELD_VEC_ENTRY("_PID=", &pid
, &pid_len
),
456 PARSE_FIELD_VEC_ENTRY("_COMM=", &comm
, &comm_len
),
457 PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message
, &message_len
),
458 PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority
, &priority_len
),
459 PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport
, &transport_len
),
460 PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname
, &hostname_len
),
461 PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid
, &fake_pid_len
),
462 PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier
, &identifier_len
),
463 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime
, &realtime_len
),
464 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic
, &monotonic_len
),
465 PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file
, &config_file_len
),
466 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit
, &unit_len
),
467 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit
, &user_unit_len
),
468 PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url
, &documentation_url_len
),
470 size_t highlight_shifted
[] = {highlight
? highlight
[0] : 0, highlight
? highlight
[1] : 0};
475 /* Set the threshold to one bigger than the actual print
476 * threshold, so that if the line is actually longer than what
477 * we're willing to print, ellipsization will occur. This way
478 * we won't output a misleading line without any indication of
481 sd_journal_set_data_threshold(j
, flags
& (OUTPUT_SHOW_ALL
|OUTPUT_FULL_WIDTH
) ? 0 : PRINT_CHAR_THRESHOLD
+ 1);
483 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
484 r
= parse_fieldv(data
, length
, fields
, ELEMENTSOF(fields
));
489 log_debug_errno(r
, "Skipping message we can't read: %m");
493 return log_error_errno(r
, "Failed to get journal fields: %m");
496 log_debug("Skipping message without MESSAGE= field.");
500 if (!(flags
& OUTPUT_SHOW_ALL
))
501 strip_tab_ansi(&message
, &message_len
, highlight_shifted
);
503 if (priority_len
== 1 && *priority
>= '0' && *priority
<= '7')
506 audit
= streq_ptr(transport
, "audit");
508 if (mode
== OUTPUT_SHORT_MONOTONIC
)
509 r
= output_timestamp_monotonic(f
, j
, monotonic
);
511 r
= output_timestamp_realtime(f
, j
, mode
, flags
, realtime
);
516 if (flags
& OUTPUT_NO_HOSTNAME
) {
517 /* Suppress display of the hostname if this is requested. */
518 hostname
= mfree(hostname
);
522 if (hostname
&& shall_print(hostname
, hostname_len
, flags
)) {
523 fprintf(f
, " %.*s", (int) hostname_len
, hostname
);
524 n
+= hostname_len
+ 1;
527 if (mode
== OUTPUT_WITH_UNIT
&& ((unit
&& shall_print(unit
, unit_len
, flags
)) ||
528 (user_unit
&& shall_print(user_unit
, user_unit_len
, flags
)))) {
530 fprintf(f
, " %.*s", (int) unit_len
, unit
);
535 fprintf(f
, "/%.*s", (int) user_unit_len
, user_unit
);
537 fprintf(f
, " %.*s", (int) user_unit_len
, user_unit
);
540 } else if (identifier
&& shall_print(identifier
, identifier_len
, flags
)) {
541 fprintf(f
, " %.*s", (int) identifier_len
, identifier
);
542 n
+= identifier_len
+ 1;
543 } else if (comm
&& shall_print(comm
, comm_len
, flags
)) {
544 fprintf(f
, " %.*s", (int) comm_len
, comm
);
547 fputs(" unknown", f
);
549 if (pid
&& shall_print(pid
, pid_len
, flags
)) {
550 fprintf(f
, "[%.*s]", (int) pid_len
, pid
);
552 } else if (fake_pid
&& shall_print(fake_pid
, fake_pid_len
, flags
)) {
553 fprintf(f
, "[%.*s]", (int) fake_pid_len
, fake_pid
);
554 n
+= fake_pid_len
+ 2;
559 if (urlify_enabled()) {
560 _cleanup_free_
char *c
= NULL
;
562 /* Insert a hyperlink to a documentation URL before the message. Note that we don't make the
563 * whole message a hyperlink, since otherwise the whole screen might end up being just
564 * hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the
565 * config file, see below) hence let's keep the documentation URL link separate. */
567 if (documentation_url
&& shall_print(documentation_url
, documentation_url_len
, flags
)) {
568 c
= strndup(documentation_url
, documentation_url_len
);
572 if (!documentation_url_is_valid(c
)) /* Eat up invalid links */
577 (void) url_from_catalog(j
, &c
); /* Acquire from catalog if not embedded in log message itself */
580 _cleanup_free_
char *urlified
= NULL
;
582 if (terminal_urlify(c
, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK
), &urlified
) >= 0) {
589 if (!(flags
& OUTPUT_SHOW_ALL
) && !utf8_is_printable(message
, message_len
)) {
590 char bytes
[FORMAT_BYTES_MAX
];
591 fprintf(f
, "[%s blob data]\n", format_bytes(bytes
, sizeof(bytes
), message_len
));
594 /* URLify config_file string in message, if the message starts with it.
595 * Skip URLification if the highlighted pattern overlaps. */
597 message_len
>= config_file_len
&&
598 memcmp(message
, config_file
, config_file_len
) == 0 &&
599 (message_len
== config_file_len
|| IN_SET(message
[config_file_len
], ':', ' ')) &&
600 (!highlight
|| highlight_shifted
[0] == 0 || highlight_shifted
[0] > config_file_len
)) {
602 _cleanup_free_
char *t
= NULL
, *urlified
= NULL
;
604 t
= strndup(config_file
, config_file_len
);
605 if (t
&& terminal_urlify_path(t
, NULL
, &urlified
) >= 0) {
606 size_t urlified_len
= strlen(urlified
);
607 size_t shift
= urlified_len
- config_file_len
;
610 joined
= realloc(urlified
, message_len
+ shift
);
612 memcpy(joined
+ urlified_len
, message
+ config_file_len
, message_len
- config_file_len
);
613 free_and_replace(message
, joined
);
615 message_len
+= shift
;
617 highlight_shifted
[0] += shift
;
618 highlight_shifted
[1] += shift
;
625 print_multiline(f
, n
+ 2, n_columns
, flags
, p
, audit
,
626 message
, message_len
,
630 if (flags
& OUTPUT_CATALOG
)
631 (void) print_catalog(f
, j
);
636 static int output_verbose(
642 const Set
*output_fields
,
643 const size_t highlight
[2]) {
647 _cleanup_free_
char *cursor
= NULL
;
648 uint64_t realtime
= 0;
649 char ts
[FORMAT_TIMESTAMP_MAX
+ 7];
650 const char *timestamp
;
656 sd_journal_set_data_threshold(j
, 0);
658 r
= sd_journal_get_data(j
, "_SOURCE_REALTIME_TIMESTAMP", &data
, &length
);
660 log_debug("Source realtime timestamp not found");
662 return log_full_errno(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_ERR
, r
, "Failed to get source realtime timestamp: %m");
664 _cleanup_free_
char *value
= NULL
;
666 r
= parse_field(data
, length
, "_SOURCE_REALTIME_TIMESTAMP=",
667 STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value
,
673 r
= safe_atou64(value
, &realtime
);
675 log_debug_errno(r
, "Failed to parse realtime timestamp: %m");
679 r
= sd_journal_get_realtime_usec(j
, &realtime
);
681 return log_full_errno(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_ERR
, r
, "Failed to get realtime timestamp: %m");
684 r
= sd_journal_get_cursor(j
, &cursor
);
686 return log_error_errno(r
, "Failed to get cursor: %m");
688 timestamp
= flags
& OUTPUT_UTC
? format_timestamp_us_utc(ts
, sizeof ts
, realtime
)
689 : format_timestamp_us(ts
, sizeof ts
, realtime
);
690 fprintf(f
, "%s [%s]\n",
691 timestamp
?: "(no timestamp)",
694 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
697 const char *on
= "", *off
= "";
698 _cleanup_free_
char *urlified
= NULL
;
701 c
= memchr(data
, '=', length
);
703 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
705 fieldlen
= c
- (const char*) data
;
707 r
= field_set_test(output_fields
, data
, fieldlen
);
713 valuelen
= length
- 1 - fieldlen
;
715 if ((flags
& OUTPUT_COLOR
) && (p
= startswith(data
, "MESSAGE="))) {
718 } else if ((p
= startswith(data
, "CONFIG_FILE="))) {
719 if (terminal_urlify_path(p
, NULL
, &urlified
) >= 0) {
721 valuelen
= strlen(urlified
);
726 if ((flags
& OUTPUT_SHOW_ALL
) ||
727 (((length
< PRINT_CHAR_THRESHOLD
) || flags
& OUTPUT_FULL_WIDTH
)
728 && utf8_is_printable(data
, length
))) {
729 fprintf(f
, " %s%.*s=", on
, fieldlen
, (const char*)data
);
730 print_multiline(f
, 4 + fieldlen
+ 1, 0, OUTPUT_FULL_WIDTH
, 0, false,
735 char bytes
[FORMAT_BYTES_MAX
];
737 fprintf(f
, " %s%.*s=[%s blob data]%s\n",
739 (int) (c
- (const char*) data
),
741 format_bytes(bytes
, sizeof(bytes
), length
- (c
- (const char *) data
) - 1),
749 if (flags
& OUTPUT_CATALOG
)
750 (void) print_catalog(f
, j
);
755 static int output_export(
761 const Set
*output_fields
,
762 const size_t highlight
[2]) {
765 char sid
[SD_ID128_STRING_MAX
];
767 usec_t realtime
, monotonic
;
768 _cleanup_free_
char *cursor
= NULL
;
774 sd_journal_set_data_threshold(j
, 0);
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");
790 "__REALTIME_TIMESTAMP="USEC_FMT
"\n"
791 "__MONOTONIC_TIMESTAMP="USEC_FMT
"\n"
796 sd_id128_to_string(boot_id
, sid
));
798 JOURNAL_FOREACH_DATA_RETVAL(j
, data
, length
, r
) {
801 /* We already printed the boot id from the data in the header, hence let's suppress it here */
802 if (memory_startswith(data
, length
, "_BOOT_ID="))
805 c
= memchr(data
, '=', length
);
807 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
810 r
= field_set_test(output_fields
, data
, c
- (const char *) data
);
816 if (utf8_is_printable_newline(data
, length
, false))
817 fwrite(data
, length
, 1, f
);
821 fwrite(data
, c
- (const char*) data
, 1, f
);
823 le64
= htole64(length
- (c
- (const char*) data
) - 1);
824 fwrite(&le64
, sizeof(le64
), 1, f
);
825 fwrite(c
+ 1, length
- (c
- (const char*) data
) - 1, 1, f
);
831 log_debug_errno(r
, "Skipping message we can't read: %m");
852 if (!(flags
& OUTPUT_SHOW_ALL
) && l
>= JSON_THRESHOLD
)
855 else if (!(flags
& OUTPUT_SHOW_ALL
) && !utf8_is_printable(p
, l
)) {
856 bool not_first
= false;
862 fprintf(f
, ", %u", (uint8_t) *p
);
865 fprintf(f
, "%u", (uint8_t) *p
);
877 if (IN_SET(*p
, '"', '\\')) {
880 } else if (*p
== '\n')
882 else if ((uint8_t) *p
< ' ')
883 fprintf(f
, "\\u%04x", (uint8_t) *p
);
898 JsonVariant
* values
[];
901 static int update_json_data(
908 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
912 if (!(flags
& OUTPUT_SHOW_ALL
) && strlen(name
) + 1 + size
>= JSON_THRESHOLD
)
913 r
= json_variant_new_null(&v
);
914 else if (utf8_is_printable(value
, size
))
915 r
= json_variant_new_stringn(&v
, value
, size
);
917 r
= json_variant_new_array_bytes(&v
, value
, size
);
919 return log_error_errno(r
, "Failed to allocate JSON data: %m");
921 d
= hashmap_get(h
, name
);
925 w
= realloc(d
, offsetof(struct json_data
, values
) + sizeof(JsonVariant
*) * (d
->n_values
+ 1));
930 assert_se(hashmap_update(h
, json_variant_string(d
->name
), d
) >= 0);
932 _cleanup_(json_variant_unrefp
) JsonVariant
*n
= NULL
;
934 r
= json_variant_new_string(&n
, name
);
936 return log_error_errno(r
, "Failed to allocate JSON name variant: %m");
938 d
= malloc0(offsetof(struct json_data
, values
) + sizeof(JsonVariant
*));
942 r
= hashmap_put(h
, json_variant_string(n
), d
);
945 return log_error_errno(r
, "Failed to insert JSON name into hashmap: %m");
948 d
->name
= TAKE_PTR(n
);
951 d
->values
[d
->n_values
++] = TAKE_PTR(v
);
955 static int update_json_data_split(
958 const Set
*output_fields
,
966 assert(data
|| size
== 0);
968 if (memory_startswith(data
, size
, "_BOOT_ID="))
971 eq
= memchr(data
, '=', MIN(size
, JSON_THRESHOLD
));
978 name
= strndupa(data
, eq
- (const char*) data
);
979 if (output_fields
&& !set_contains(output_fields
, name
))
982 return update_json_data(h
, flags
, name
, eq
+ 1, size
- (eq
- (const char*) data
) - 1);
985 static int output_json(
991 const Set
*output_fields
,
992 const size_t highlight
[2]) {
994 char sid
[SD_ID128_STRING_MAX
], usecbuf
[DECIMAL_STR_MAX(usec_t
)];
995 _cleanup_(json_variant_unrefp
) JsonVariant
*object
= NULL
;
996 _cleanup_free_
char *cursor
= NULL
;
997 uint64_t realtime
, monotonic
;
998 JsonVariant
**array
= NULL
;
1008 (void) sd_journal_set_data_threshold(j
, flags
& OUTPUT_SHOW_ALL
? 0 : JSON_THRESHOLD
);
1010 r
= sd_journal_get_realtime_usec(j
, &realtime
);
1012 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
1014 r
= sd_journal_get_monotonic_usec(j
, &monotonic
, &boot_id
);
1016 return log_error_errno(r
, "Failed to get monotonic timestamp: %m");
1018 r
= sd_journal_get_cursor(j
, &cursor
);
1020 return log_error_errno(r
, "Failed to get cursor: %m");
1022 h
= hashmap_new(&string_hash_ops
);
1026 r
= update_json_data(h
, flags
, "__CURSOR", cursor
, strlen(cursor
));
1030 xsprintf(usecbuf
, USEC_FMT
, realtime
);
1031 r
= update_json_data(h
, flags
, "__REALTIME_TIMESTAMP", usecbuf
, strlen(usecbuf
));
1035 xsprintf(usecbuf
, USEC_FMT
, monotonic
);
1036 r
= update_json_data(h
, flags
, "__MONOTONIC_TIMESTAMP", usecbuf
, strlen(usecbuf
));
1040 sd_id128_to_string(boot_id
, sid
);
1041 r
= update_json_data(h
, flags
, "_BOOT_ID", sid
, strlen(sid
));
1049 r
= sd_journal_enumerate_data(j
, &data
, &size
);
1050 if (r
== -EBADMSG
) {
1051 log_debug_errno(r
, "Skipping message we can't read: %m");
1056 log_error_errno(r
, "Failed to read journal: %m");
1062 r
= update_json_data_split(h
, flags
, output_fields
, data
, size
);
1067 array
= new(JsonVariant
*, hashmap_size(h
)*2);
1073 HASHMAP_FOREACH(d
, h
, i
) {
1074 assert(d
->n_values
> 0);
1076 array
[n
++] = json_variant_ref(d
->name
);
1078 if (d
->n_values
== 1)
1079 array
[n
++] = json_variant_ref(d
->values
[0]);
1081 _cleanup_(json_variant_unrefp
) JsonVariant
*q
= NULL
;
1083 r
= json_variant_new_array(&q
, d
->values
, d
->n_values
);
1085 log_error_errno(r
, "Failed to create JSON array: %m");
1089 array
[n
++] = TAKE_PTR(q
);
1093 r
= json_variant_new_object(&object
, array
, n
);
1095 log_error_errno(r
, "Failed to allocate JSON object: %m");
1099 json_variant_dump(object
,
1100 output_mode_to_json_format_flags(mode
) |
1101 (FLAGS_SET(flags
, OUTPUT_COLOR
) ? JSON_FORMAT_COLOR
: 0),
1107 while ((d
= hashmap_steal_first(h
))) {
1110 json_variant_unref(d
->name
);
1111 for (k
= 0; k
< d
->n_values
; k
++)
1112 json_variant_unref(d
->values
[k
]);
1119 json_variant_unref_many(array
, n
);
1125 static int output_cat_field(
1131 const size_t highlight
[2]) {
1133 const char *color_on
= "", *color_off
= "", *highlight_on
= "";
1138 if (FLAGS_SET(flags
, OUTPUT_COLOR
))
1139 get_log_colors(prio
, &color_on
, &color_off
, &highlight_on
);
1141 r
= sd_journal_get_data(j
, field
, &data
, &l
);
1142 if (r
== -EBADMSG
) {
1143 log_debug_errno(r
, "Skipping message we can't read: %m");
1146 if (r
== -ENOENT
) /* An entry without the requested field */
1149 return log_error_errno(r
, "Failed to get data: %m");
1152 assert(l
>= fl
+ 1);
1153 assert(((char*) data
)[fl
] == '=');
1155 data
= (const uint8_t*) data
+ fl
+ 1;
1158 if (FLAGS_SET(flags
, OUTPUT_COLOR
)) {
1160 assert(highlight
[0] <= highlight
[1]);
1161 assert(highlight
[1] <= l
);
1164 fwrite((const char*) data
, 1, highlight
[0], f
);
1165 fputs(highlight_on
, f
);
1166 fwrite((const char*) data
+ highlight
[0], 1, highlight
[1] - highlight
[0], f
);
1168 fwrite((const char*) data
+ highlight
[1], 1, l
- highlight
[1], f
);
1169 fputs(color_off
, f
);
1172 fwrite((const char*) data
, 1, l
, f
);
1173 fputs(color_off
, f
);
1176 fwrite((const char*) data
, 1, l
, f
);
1182 static int output_cat(
1188 const Set
*output_fields
,
1189 const size_t highlight
[2]) {
1191 int r
, prio
= LOG_INFO
;
1198 (void) sd_journal_set_data_threshold(j
, 0);
1200 if (FLAGS_SET(flags
, OUTPUT_COLOR
)) {
1204 /* Determine priority of this entry, so that we can color it nicely */
1206 r
= sd_journal_get_data(j
, "PRIORITY", &data
, &l
);
1207 if (r
== -EBADMSG
) {
1208 log_debug_errno(r
, "Skipping message we can't read: %m");
1213 return log_error_errno(r
, "Failed to get data: %m");
1215 /* An entry without PRIORITY */
1216 } else if (l
== 10 && memcmp(data
, "PRIORITY=", 9) == 0) {
1217 char c
= ((char*) data
)[9];
1219 if (c
>= '0' && c
<= '7')
1224 if (set_isempty(output_fields
))
1225 return output_cat_field(f
, j
, flags
, prio
, "MESSAGE", highlight
);
1227 SET_FOREACH(field
, output_fields
, iterator
) {
1228 r
= output_cat_field(f
, j
, flags
, prio
, field
, streq(field
, "MESSAGE") ? highlight
: NULL
);
1236 static int (*output_funcs
[_OUTPUT_MODE_MAX
])(
1242 const Set
*output_fields
,
1243 const size_t highlight
[2]) = {
1245 [OUTPUT_SHORT
] = output_short
,
1246 [OUTPUT_SHORT_ISO
] = output_short
,
1247 [OUTPUT_SHORT_ISO_PRECISE
] = output_short
,
1248 [OUTPUT_SHORT_PRECISE
] = output_short
,
1249 [OUTPUT_SHORT_MONOTONIC
] = output_short
,
1250 [OUTPUT_SHORT_UNIX
] = output_short
,
1251 [OUTPUT_SHORT_FULL
] = output_short
,
1252 [OUTPUT_VERBOSE
] = output_verbose
,
1253 [OUTPUT_EXPORT
] = output_export
,
1254 [OUTPUT_JSON
] = output_json
,
1255 [OUTPUT_JSON_PRETTY
] = output_json
,
1256 [OUTPUT_JSON_SSE
] = output_json
,
1257 [OUTPUT_JSON_SEQ
] = output_json
,
1258 [OUTPUT_CAT
] = output_cat
,
1259 [OUTPUT_WITH_UNIT
] = output_short
,
1262 int show_journal_entry(
1268 char **output_fields
,
1269 const size_t highlight
[2],
1272 _cleanup_set_free_ Set
*fields
= NULL
;
1276 assert(mode
< _OUTPUT_MODE_MAX
);
1279 n_columns
= columns();
1281 r
= set_put_strdupv(&fields
, output_fields
);
1285 r
= output_funcs
[mode
](f
, j
, mode
, n_columns
, flags
, fields
, highlight
);
1287 if (ellipsized
&& r
> 0)
1293 static int maybe_print_begin_newline(FILE *f
, OutputFlags
*flags
) {
1297 if (!(*flags
& OUTPUT_BEGIN_NEWLINE
))
1300 /* Print a beginning new line if that's request, but only once
1301 * on the first line we print. */
1304 *flags
&= ~OUTPUT_BEGIN_NEWLINE
;
1320 bool need_seek
= false;
1321 int warn_cutoff
= flags
& OUTPUT_WARN_CUTOFF
;
1325 assert(mode
< _OUTPUT_MODE_MAX
);
1327 if (how_many
== (unsigned) -1)
1331 r
= sd_journal_seek_tail(j
);
1333 return log_error_errno(r
, "Failed to seek to tail: %m");
1335 r
= sd_journal_previous_skip(j
, how_many
);
1337 return log_error_errno(r
, "Failed to skip previous: %m");
1344 r
= sd_journal_next(j
);
1346 return log_error_errno(r
, "Failed to iterate through journal: %m");
1354 if (not_before
> 0) {
1355 r
= sd_journal_get_monotonic_usec(j
, &usec
, NULL
);
1357 /* -ESTALE is returned if the timestamp is not from this boot */
1361 return log_error_errno(r
, "Failed to get journal time: %m");
1363 if (usec
< not_before
)
1368 maybe_print_begin_newline(f
, &flags
);
1370 r
= show_journal_entry(f
, j
, mode
, n_columns
, flags
, NULL
, NULL
, ellipsized
);
1375 if (warn_cutoff
&& line
< how_many
&& not_before
> 0) {
1379 /* Check whether the cutoff line is too early */
1381 r
= sd_id128_get_boot(&boot_id
);
1383 return log_error_errno(r
, "Failed to get boot id: %m");
1385 r
= sd_journal_get_cutoff_monotonic_usec(j
, boot_id
, &cutoff
, NULL
);
1387 return log_error_errno(r
, "Failed to get journal cutoff time: %m");
1389 if (r
> 0 && not_before
< cutoff
) {
1390 maybe_print_begin_newline(f
, &flags
);
1392 /* If we logged *something* and no permission error happened, than we can reliably
1393 * emit the warning about rotation. If we didn't log anything and access errors
1394 * happened, emit hint about permissions. Otherwise, give a generic message, since we
1395 * can't diagnose the issue. */
1397 bool noaccess
= journal_access_blocked(j
);
1399 if (line
== 0 && noaccess
)
1400 fprintf(f
, "Warning: some journal files were not opened due to insufficient permissions.");
1402 fprintf(f
, "Warning: journal has been rotated since unit was started, output may be incomplete.\n");
1404 fprintf(f
, "Warning: journal has been rotated since unit was started and some journal "
1405 "files were not opened due to insufficient permissions, output may be incomplete.\n");
1408 warn_cutoff
= false;
1414 int add_matches_for_unit(sd_journal
*j
, const char *unit
) {
1415 const char *m1
, *m2
, *m3
, *m4
;
1421 m1
= strjoina("_SYSTEMD_UNIT=", unit
);
1422 m2
= strjoina("COREDUMP_UNIT=", unit
);
1423 m3
= strjoina("UNIT=", unit
);
1424 m4
= strjoina("OBJECT_SYSTEMD_UNIT=", unit
);
1427 /* Look for messages from the service itself */
1428 (r
= sd_journal_add_match(j
, m1
, 0)) ||
1430 /* Look for coredumps of the service */
1431 (r
= sd_journal_add_disjunction(j
)) ||
1432 (r
= sd_journal_add_match(j
, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1433 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1434 (r
= sd_journal_add_match(j
, m2
, 0)) ||
1436 /* Look for messages from PID 1 about this service */
1437 (r
= sd_journal_add_disjunction(j
)) ||
1438 (r
= sd_journal_add_match(j
, "_PID=1", 0)) ||
1439 (r
= sd_journal_add_match(j
, m3
, 0)) ||
1441 /* Look for messages from authorized daemons about this service */
1442 (r
= sd_journal_add_disjunction(j
)) ||
1443 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1444 (r
= sd_journal_add_match(j
, m4
, 0))
1447 if (r
== 0 && endswith(unit
, ".slice")) {
1450 m5
= strjoina("_SYSTEMD_SLICE=", unit
);
1452 /* Show all messages belonging to a slice */
1454 (r
= sd_journal_add_disjunction(j
)) ||
1455 (r
= sd_journal_add_match(j
, m5
, 0))
1462 int add_matches_for_user_unit(sd_journal
*j
, const char *unit
, uid_t uid
) {
1464 char *m1
, *m2
, *m3
, *m4
;
1465 char muid
[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t
)];
1470 m1
= strjoina("_SYSTEMD_USER_UNIT=", unit
);
1471 m2
= strjoina("USER_UNIT=", unit
);
1472 m3
= strjoina("COREDUMP_USER_UNIT=", unit
);
1473 m4
= strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit
);
1474 sprintf(muid
, "_UID="UID_FMT
, uid
);
1477 /* Look for messages from the user service itself */
1478 (r
= sd_journal_add_match(j
, m1
, 0)) ||
1479 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1481 /* Look for messages from systemd about this service */
1482 (r
= sd_journal_add_disjunction(j
)) ||
1483 (r
= sd_journal_add_match(j
, m2
, 0)) ||
1484 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1486 /* Look for coredumps of the service */
1487 (r
= sd_journal_add_disjunction(j
)) ||
1488 (r
= sd_journal_add_match(j
, m3
, 0)) ||
1489 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1490 (r
= sd_journal_add_match(j
, "_UID=0", 0)) ||
1492 /* Look for messages from authorized daemons about this service */
1493 (r
= sd_journal_add_disjunction(j
)) ||
1494 (r
= sd_journal_add_match(j
, m4
, 0)) ||
1495 (r
= sd_journal_add_match(j
, muid
, 0)) ||
1496 (r
= sd_journal_add_match(j
, "_UID=0", 0))
1499 if (r
== 0 && endswith(unit
, ".slice")) {
1502 m5
= strjoina("_SYSTEMD_SLICE=", unit
);
1504 /* Show all messages belonging to a slice */
1506 (r
= sd_journal_add_disjunction(j
)) ||
1507 (r
= sd_journal_add_match(j
, m5
, 0)) ||
1508 (r
= sd_journal_add_match(j
, muid
, 0))
1515 static int get_boot_id_for_machine(const char *machine
, sd_id128_t
*boot_id
) {
1516 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1517 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, rootfd
= -1;
1518 char buf
[ID128_UUID_STRING_MAX
];
1526 if (!machine_name_is_valid(machine
))
1529 r
= container_get_leader(machine
, &pid
);
1533 r
= namespace_open(pid
, &pidnsfd
, &mntnsfd
, NULL
, NULL
, &rootfd
);
1537 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1540 r
= namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG
,
1541 pidnsfd
, mntnsfd
, -1, -1, rootfd
, &child
);
1547 pair
[0] = safe_close(pair
[0]);
1549 fd
= open("/proc/sys/kernel/random/boot_id", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1551 _exit(EXIT_FAILURE
);
1553 r
= loop_read_exact(fd
, buf
, 36, false);
1556 _exit(EXIT_FAILURE
);
1558 k
= send(pair
[1], buf
, 36, MSG_NOSIGNAL
);
1560 _exit(EXIT_FAILURE
);
1562 _exit(EXIT_SUCCESS
);
1565 pair
[1] = safe_close(pair
[1]);
1567 r
= wait_for_terminate_and_check("(sd-bootidns)", child
, 0);
1570 if (r
!= EXIT_SUCCESS
)
1573 k
= recv(pair
[0], buf
, 36, 0);
1578 r
= sd_id128_from_string(buf
, boot_id
);
1585 int add_match_this_boot(sd_journal
*j
, const char *machine
) {
1586 char match
[9+32+1] = "_BOOT_ID=";
1593 r
= get_boot_id_for_machine(machine
, &boot_id
);
1595 return log_error_errno(r
, "Failed to get boot id of container %s: %m", machine
);
1597 r
= sd_id128_get_boot(&boot_id
);
1599 return log_error_errno(r
, "Failed to get boot id: %m");
1602 sd_id128_to_string(boot_id
, match
+ 9);
1603 r
= sd_journal_add_match(j
, match
, strlen(match
));
1605 return log_error_errno(r
, "Failed to add match: %m");
1607 r
= sd_journal_add_conjunction(j
);
1609 return log_error_errno(r
, "Failed to add conjunction: %m");
1614 int show_journal_by_unit(
1617 const char *log_namespace
,
1624 int journal_open_flags
,
1628 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1632 assert(mode
< _OUTPUT_MODE_MAX
);
1638 r
= sd_journal_open_namespace(&j
, log_namespace
, journal_open_flags
| SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE
);
1640 return log_error_errno(r
, "Failed to open journal: %m");
1642 r
= add_match_this_boot(j
, NULL
);
1647 r
= add_matches_for_unit(j
, unit
);
1649 r
= add_matches_for_user_unit(j
, unit
, uid
);
1651 return log_error_errno(r
, "Failed to add unit matches: %m");
1653 if (DEBUG_LOGGING
) {
1654 _cleanup_free_
char *filter
;
1656 filter
= journal_make_match_string(j
);
1660 log_debug("Journal filter: %s", filter
);
1663 return show_journal(f
, j
, mode
, n_columns
, not_before
, how_many
, flags
, ellipsized
);