1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
33 #include <sys/inotify.h>
38 # define PCRE2_CODE_UNIT_WIDTH 8
43 #include "sd-journal.h"
46 #include "alloc-util.h"
47 #include "bus-error.h"
50 #include "chattr-util.h"
55 #include "glob-util.h"
56 #include "hostname-util.h"
58 #include "journal-def.h"
59 #include "journal-internal.h"
60 #include "journal-qrcode.h"
61 #include "journal-util.h"
62 #include "journal-vacuum.h"
63 #include "journal-verify.h"
64 #include "locale-util.h"
66 #include "logs-show.h"
69 #include "parse-util.h"
70 #include "path-util.h"
71 #include "rlimit-util.h"
75 #include "syslog-util.h"
76 #include "terminal-util.h"
78 #include "udev-util.h"
79 #include "unit-name.h"
80 #include "user-util.h"
82 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
84 #define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */
87 DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data
*, pcre2_match_data_free
);
88 DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code
*, pcre2_code_free
);
90 static int pattern_compile(const char *pattern
, unsigned flags
, pcre2_code
**out
) {
92 PCRE2_SIZE erroroffset
;
95 p
= pcre2_compile((PCRE2_SPTR8
) pattern
,
96 PCRE2_ZERO_TERMINATED
, flags
, &errorcode
, &erroroffset
, NULL
);
98 unsigned char buf
[LINE_MAX
];
100 r
= pcre2_get_error_message(errorcode
, buf
, sizeof buf
);
102 log_error("Bad pattern \"%s\": %s",
104 r
< 0 ? "unknown error" : (char*) buf
);
115 /* Special values for arg_lines */
116 ARG_LINES_DEFAULT
= -2,
120 static OutputMode arg_output
= OUTPUT_SHORT
;
121 static bool arg_utc
= false;
122 static bool arg_pager_end
= false;
123 static bool arg_follow
= false;
124 static bool arg_full
= true;
125 static bool arg_all
= false;
126 static bool arg_no_pager
= false;
127 static int arg_lines
= ARG_LINES_DEFAULT
;
128 static bool arg_no_tail
= false;
129 static bool arg_quiet
= false;
130 static bool arg_merge
= false;
131 static bool arg_boot
= false;
132 static sd_id128_t arg_boot_id
= {};
133 static int arg_boot_offset
= 0;
134 static bool arg_dmesg
= false;
135 static bool arg_no_hostname
= false;
136 static const char *arg_cursor
= NULL
;
137 static const char *arg_after_cursor
= NULL
;
138 static bool arg_show_cursor
= false;
139 static const char *arg_directory
= NULL
;
140 static char **arg_file
= NULL
;
141 static bool arg_file_stdin
= false;
142 static int arg_priorities
= 0xFF;
143 static char *arg_verify_key
= NULL
;
145 static usec_t arg_interval
= DEFAULT_FSS_INTERVAL_USEC
;
146 static bool arg_force
= false;
148 static usec_t arg_since
, arg_until
;
149 static bool arg_since_set
= false, arg_until_set
= false;
150 static char **arg_syslog_identifier
= NULL
;
151 static char **arg_system_units
= NULL
;
152 static char **arg_user_units
= NULL
;
153 static const char *arg_field
= NULL
;
154 static bool arg_catalog
= false;
155 static bool arg_reverse
= false;
156 static int arg_journal_type
= 0;
157 static char *arg_root
= NULL
;
158 static const char *arg_machine
= NULL
;
159 static uint64_t arg_vacuum_size
= 0;
160 static uint64_t arg_vacuum_n_files
= 0;
161 static usec_t arg_vacuum_time
= 0;
162 static char **arg_output_fields
= NULL
;
165 static const char *arg_pattern
= NULL
;
166 static pcre2_code
*arg_compiled_pattern
= NULL
;
167 static int arg_case_sensitive
= -1; /* -1 means be smart */
179 ACTION_UPDATE_CATALOG
,
186 ACTION_LIST_FIELD_NAMES
,
187 } arg_action
= ACTION_SHOW
;
189 typedef struct BootId
{
193 LIST_FIELDS(struct BootId
, boot_list
);
196 static int add_matches_for_device(sd_journal
*j
, const char *devpath
) {
198 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
199 _cleanup_udev_device_unref_
struct udev_device
*device
= NULL
;
200 struct udev_device
*d
= NULL
;
206 if (!path_startswith(devpath
, "/dev/")) {
207 log_error("Devpath does not start with /dev/");
215 r
= stat(devpath
, &st
);
217 log_error_errno(errno
, "Couldn't stat file: %m");
219 d
= device
= udev_device_new_from_devnum(udev
, S_ISBLK(st
.st_mode
) ? 'b' : 'c', st
.st_rdev
);
221 return log_error_errno(errno
, "Failed to get udev device from devnum %u:%u: %m", major(st
.st_rdev
), minor(st
.st_rdev
));
224 _cleanup_free_
char *match
= NULL
;
225 const char *subsys
, *sysname
, *devnode
;
227 subsys
= udev_device_get_subsystem(d
);
229 d
= udev_device_get_parent(d
);
233 sysname
= udev_device_get_sysname(d
);
235 d
= udev_device_get_parent(d
);
239 match
= strjoin("_KERNEL_DEVICE=+", subsys
, ":", sysname
);
243 r
= sd_journal_add_match(j
, match
, 0);
245 return log_error_errno(r
, "Failed to add match: %m");
247 devnode
= udev_device_get_devnode(d
);
249 _cleanup_free_
char *match1
= NULL
;
251 r
= stat(devnode
, &st
);
253 return log_error_errno(r
, "Failed to stat() device node \"%s\": %m", devnode
);
255 r
= asprintf(&match1
, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st
.st_mode
) ? 'b' : 'c', major(st
.st_rdev
), minor(st
.st_rdev
));
259 r
= sd_journal_add_match(j
, match1
, 0);
261 return log_error_errno(r
, "Failed to add match: %m");
264 d
= udev_device_get_parent(d
);
267 r
= add_match_this_boot(j
, arg_machine
);
269 return log_error_errno(r
, "Failed to add match for the current boot: %m");
274 static char *format_timestamp_maybe_utc(char *buf
, size_t l
, usec_t t
) {
277 return format_timestamp_utc(buf
, l
, t
);
279 return format_timestamp(buf
, l
, t
);
282 static int parse_boot_descriptor(const char *x
, sd_id128_t
*boot_id
, int *offset
) {
283 sd_id128_t id
= SD_ID128_NULL
;
286 if (strlen(x
) >= 32) {
290 r
= sd_id128_from_string(t
, &id
);
294 if (!IN_SET(*x
, 0, '-', '+'))
298 r
= safe_atoi(x
, &off
);
303 r
= safe_atoi(x
, &off
);
317 static void help(void) {
319 (void) pager_open(arg_no_pager
, arg_pager_end
);
321 printf("%s [OPTIONS...] [MATCHES...]\n\n"
322 "Query the journal.\n\n"
324 " --system Show the system journal\n"
325 " --user Show the user journal for the current user\n"
326 " -M --machine=CONTAINER Operate on local container\n"
327 " -S --since=DATE Show entries not older than the specified date\n"
328 " -U --until=DATE Show entries not newer than the specified date\n"
329 " -c --cursor=CURSOR Show entries starting at the specified cursor\n"
330 " --after-cursor=CURSOR Show entries after the specified cursor\n"
331 " --show-cursor Print the cursor after all the entries\n"
332 " -b --boot[=ID] Show current boot or the specified boot\n"
333 " --list-boots Show terse information about recorded boots\n"
334 " -k --dmesg Show kernel message log from the current boot\n"
335 " -u --unit=UNIT Show logs from the specified unit\n"
336 " --user-unit=UNIT Show logs from the specified user unit\n"
337 " -t --identifier=STRING Show entries with the specified syslog identifier\n"
338 " -p --priority=RANGE Show entries with the specified priority\n"
339 " -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
340 " --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
341 " -e --pager-end Immediately jump to the end in the pager\n"
342 " -f --follow Follow the journal\n"
343 " -n --lines[=INTEGER] Number of journal entries to show\n"
344 " --no-tail Show all lines, even in follow mode\n"
345 " -r --reverse Show the newest entries first\n"
346 " -o --output=STRING Change journal output mode (short, short-precise,\n"
347 " short-iso, short-iso-precise, short-full,\n"
348 " short-monotonic, short-unix, verbose, export,\n"
349 " json, json-pretty, json-sse, cat)\n"
350 " --output-fields=LIST Select fields to print in verbose/export/json modes\n"
351 " --utc Express time in Coordinated Universal Time (UTC)\n"
352 " -x --catalog Add message explanations where available\n"
353 " --no-full Ellipsize fields\n"
354 " -a --all Show all fields, including long and unprintable\n"
355 " -q --quiet Do not show info messages and privilege warning\n"
356 " --no-pager Do not pipe output into a pager\n"
357 " --no-hostname Suppress output of hostname field\n"
358 " -m --merge Show entries from all available journals\n"
359 " -D --directory=PATH Show journal files from directory\n"
360 " --file=PATH Show journal file\n"
361 " --root=ROOT Operate on files below a root directory\n"
363 " --interval=TIME Time interval for changing the FSS sealing key\n"
364 " --verify-key=KEY Specify FSS verification key\n"
365 " --force Override of the FSS key pair with --setup-keys\n"
368 " -h --help Show this help text\n"
369 " --version Show package version\n"
370 " -N --fields List all field names currently used\n"
371 " -F --field=FIELD List all values that a specified field takes\n"
372 " --disk-usage Show total disk usage of all journal files\n"
373 " --vacuum-size=BYTES Reduce disk usage below specified size\n"
374 " --vacuum-files=INT Leave only the specified number of journal files\n"
375 " --vacuum-time=TIME Remove journal files older than specified time\n"
376 " --verify Verify journal file consistency\n"
377 " --sync Synchronize unwritten journal messages to disk\n"
378 " --flush Flush all journal data from /run into /var\n"
379 " --rotate Request immediate rotation of the journal files\n"
380 " --header Show journal header information\n"
381 " --list-catalog Show all message IDs in the catalog\n"
382 " --dump-catalog Show entries in the message catalog\n"
383 " --update-catalog Update the message catalog database\n"
384 " --new-id128 Generate a new 128-bit ID\n"
386 " --setup-keys Generate a new FSS key pair\n"
388 , program_invocation_short_name
);
391 static int parse_argv(int argc
, char *argv
[]) {
430 static const struct option options
[] = {
431 { "help", no_argument
, NULL
, 'h' },
432 { "version" , no_argument
, NULL
, ARG_VERSION
},
433 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
434 { "pager-end", no_argument
, NULL
, 'e' },
435 { "follow", no_argument
, NULL
, 'f' },
436 { "force", no_argument
, NULL
, ARG_FORCE
},
437 { "output", required_argument
, NULL
, 'o' },
438 { "all", no_argument
, NULL
, 'a' },
439 { "full", no_argument
, NULL
, 'l' },
440 { "no-full", no_argument
, NULL
, ARG_NO_FULL
},
441 { "lines", optional_argument
, NULL
, 'n' },
442 { "no-tail", no_argument
, NULL
, ARG_NO_TAIL
},
443 { "new-id128", no_argument
, NULL
, ARG_NEW_ID128
},
444 { "quiet", no_argument
, NULL
, 'q' },
445 { "merge", no_argument
, NULL
, 'm' },
446 { "this-boot", no_argument
, NULL
, ARG_THIS_BOOT
}, /* deprecated */
447 { "boot", optional_argument
, NULL
, 'b' },
448 { "list-boots", no_argument
, NULL
, ARG_LIST_BOOTS
},
449 { "dmesg", no_argument
, NULL
, 'k' },
450 { "system", no_argument
, NULL
, ARG_SYSTEM
},
451 { "user", no_argument
, NULL
, ARG_USER
},
452 { "directory", required_argument
, NULL
, 'D' },
453 { "file", required_argument
, NULL
, ARG_FILE
},
454 { "root", required_argument
, NULL
, ARG_ROOT
},
455 { "header", no_argument
, NULL
, ARG_HEADER
},
456 { "identifier", required_argument
, NULL
, 't' },
457 { "priority", required_argument
, NULL
, 'p' },
458 { "grep", required_argument
, NULL
, 'g' },
459 { "case-sensitive", optional_argument
, NULL
, ARG_CASE_SENSITIVE
},
460 { "setup-keys", no_argument
, NULL
, ARG_SETUP_KEYS
},
461 { "interval", required_argument
, NULL
, ARG_INTERVAL
},
462 { "verify", no_argument
, NULL
, ARG_VERIFY
},
463 { "verify-key", required_argument
, NULL
, ARG_VERIFY_KEY
},
464 { "disk-usage", no_argument
, NULL
, ARG_DISK_USAGE
},
465 { "cursor", required_argument
, NULL
, 'c' },
466 { "after-cursor", required_argument
, NULL
, ARG_AFTER_CURSOR
},
467 { "show-cursor", no_argument
, NULL
, ARG_SHOW_CURSOR
},
468 { "since", required_argument
, NULL
, 'S' },
469 { "until", required_argument
, NULL
, 'U' },
470 { "unit", required_argument
, NULL
, 'u' },
471 { "user-unit", required_argument
, NULL
, ARG_USER_UNIT
},
472 { "field", required_argument
, NULL
, 'F' },
473 { "fields", no_argument
, NULL
, 'N' },
474 { "catalog", no_argument
, NULL
, 'x' },
475 { "list-catalog", no_argument
, NULL
, ARG_LIST_CATALOG
},
476 { "dump-catalog", no_argument
, NULL
, ARG_DUMP_CATALOG
},
477 { "update-catalog", no_argument
, NULL
, ARG_UPDATE_CATALOG
},
478 { "reverse", no_argument
, NULL
, 'r' },
479 { "machine", required_argument
, NULL
, 'M' },
480 { "utc", no_argument
, NULL
, ARG_UTC
},
481 { "flush", no_argument
, NULL
, ARG_FLUSH
},
482 { "sync", no_argument
, NULL
, ARG_SYNC
},
483 { "rotate", no_argument
, NULL
, ARG_ROTATE
},
484 { "vacuum-size", required_argument
, NULL
, ARG_VACUUM_SIZE
},
485 { "vacuum-files", required_argument
, NULL
, ARG_VACUUM_FILES
},
486 { "vacuum-time", required_argument
, NULL
, ARG_VACUUM_TIME
},
487 { "no-hostname", no_argument
, NULL
, ARG_NO_HOSTNAME
},
488 { "output-fields", required_argument
, NULL
, ARG_OUTPUT_FIELDS
},
497 while ((c
= getopt_long(argc
, argv
, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:", options
, NULL
)) >= 0)
513 arg_pager_end
= true;
515 if (arg_lines
== ARG_LINES_DEFAULT
)
525 arg_output
= output_mode_from_string(optarg
);
526 if (arg_output
< 0) {
527 log_error("Unknown output format '%s'.", optarg
);
531 if (IN_SET(arg_output
, OUTPUT_EXPORT
, OUTPUT_JSON
, OUTPUT_JSON_PRETTY
, OUTPUT_JSON_SSE
, OUTPUT_CAT
))
550 if (streq(optarg
, "all"))
551 arg_lines
= ARG_LINES_ALL
;
553 r
= safe_atoi(optarg
, &arg_lines
);
554 if (r
< 0 || arg_lines
< 0) {
555 log_error("Failed to parse lines '%s'", optarg
);
562 /* Hmm, no argument? Maybe the next
563 * word on the command line is
564 * supposed to be the argument? Let's
565 * see if there is one, and is
569 if (streq(argv
[optind
], "all")) {
570 arg_lines
= ARG_LINES_ALL
;
572 } else if (safe_atoi(argv
[optind
], &n
) >= 0 && n
>= 0) {
586 arg_action
= ACTION_NEW_ID128
;
605 r
= parse_boot_descriptor(optarg
, &arg_boot_id
, &arg_boot_offset
);
607 log_error("Failed to parse boot descriptor '%s'", optarg
);
612 /* Hmm, no argument? Maybe the next
613 * word on the command line is
614 * supposed to be the argument? Let's
615 * see if there is one and is parsable
616 * as a boot descriptor... */
619 parse_boot_descriptor(argv
[optind
], &arg_boot_id
, &arg_boot_offset
) >= 0)
626 arg_action
= ACTION_LIST_BOOTS
;
630 arg_boot
= arg_dmesg
= true;
634 arg_journal_type
|= SD_JOURNAL_SYSTEM
;
638 arg_journal_type
|= SD_JOURNAL_CURRENT_USER
;
642 arg_machine
= optarg
;
646 arg_directory
= optarg
;
650 if (streq(optarg
, "-"))
651 /* An undocumented feature: we can read journal files from STDIN. We don't document
652 * this though, since after all we only support this for mmap-able, seekable files, and
653 * not for example pipes which are probably the primary usecase for reading things from
654 * STDIN. To avoid confusion we hence don't document this feature. */
655 arg_file_stdin
= true;
657 r
= glob_extend(&arg_file
, optarg
);
659 return log_error_errno(r
, "Failed to add paths: %m");
664 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
673 case ARG_AFTER_CURSOR
:
674 arg_after_cursor
= optarg
;
677 case ARG_SHOW_CURSOR
:
678 arg_show_cursor
= true;
682 arg_action
= ACTION_PRINT_HEADER
;
686 arg_action
= ACTION_VERIFY
;
690 arg_action
= ACTION_DISK_USAGE
;
693 case ARG_VACUUM_SIZE
:
694 r
= parse_size(optarg
, 1024, &arg_vacuum_size
);
696 log_error("Failed to parse vacuum size: %s", optarg
);
700 arg_action
= ACTION_VACUUM
;
703 case ARG_VACUUM_FILES
:
704 r
= safe_atou64(optarg
, &arg_vacuum_n_files
);
706 log_error("Failed to parse vacuum files: %s", optarg
);
710 arg_action
= ACTION_VACUUM
;
713 case ARG_VACUUM_TIME
:
714 r
= parse_sec(optarg
, &arg_vacuum_time
);
716 log_error("Failed to parse vacuum time: %s", optarg
);
720 arg_action
= ACTION_VACUUM
;
729 arg_action
= ACTION_SETUP_KEYS
;
734 arg_action
= ACTION_VERIFY
;
735 r
= free_and_strdup(&arg_verify_key
, optarg
);
738 /* Use memset not string_erase so this doesn't look confusing
739 * in ps or htop output. */
740 memset(optarg
, 'x', strlen(optarg
));
746 r
= parse_sec(optarg
, &arg_interval
);
747 if (r
< 0 || arg_interval
<= 0) {
748 log_error("Failed to parse sealing key change interval: %s", optarg
);
757 log_error("Forward-secure sealing not available.");
764 dots
= strstr(optarg
, "..");
770 a
= strndup(optarg
, dots
- optarg
);
774 from
= log_level_from_string(a
);
775 to
= log_level_from_string(dots
+ 2);
778 if (from
< 0 || to
< 0) {
779 log_error("Failed to parse log level range %s", optarg
);
786 for (i
= from
; i
<= to
; i
++)
787 arg_priorities
|= 1 << i
;
789 for (i
= to
; i
<= from
; i
++)
790 arg_priorities
|= 1 << i
;
796 p
= log_level_from_string(optarg
);
798 log_error("Unknown log level %s", optarg
);
804 for (i
= 0; i
<= p
; i
++)
805 arg_priorities
|= 1 << i
;
813 arg_pattern
= optarg
;
816 case ARG_CASE_SENSITIVE
:
818 r
= parse_boolean(optarg
);
820 return log_error_errno(r
, "Bad --case-sensitive= argument \"%s\": %m", optarg
);
821 arg_case_sensitive
= r
;
823 arg_case_sensitive
= true;
828 case ARG_CASE_SENSITIVE
:
829 return log_error("Compiled without pattern matching support");
833 r
= parse_timestamp(optarg
, &arg_since
);
835 log_error("Failed to parse timestamp: %s", optarg
);
838 arg_since_set
= true;
842 r
= parse_timestamp(optarg
, &arg_until
);
844 log_error("Failed to parse timestamp: %s", optarg
);
847 arg_until_set
= true;
851 r
= strv_extend(&arg_syslog_identifier
, optarg
);
857 r
= strv_extend(&arg_system_units
, optarg
);
863 r
= strv_extend(&arg_user_units
, optarg
);
869 arg_action
= ACTION_LIST_FIELDS
;
874 arg_action
= ACTION_LIST_FIELD_NAMES
;
877 case ARG_NO_HOSTNAME
:
878 arg_no_hostname
= true;
885 case ARG_LIST_CATALOG
:
886 arg_action
= ACTION_LIST_CATALOG
;
889 case ARG_DUMP_CATALOG
:
890 arg_action
= ACTION_DUMP_CATALOG
;
893 case ARG_UPDATE_CATALOG
:
894 arg_action
= ACTION_UPDATE_CATALOG
;
906 arg_action
= ACTION_FLUSH
;
910 arg_action
= ACTION_ROTATE
;
914 arg_action
= ACTION_SYNC
;
917 case ARG_OUTPUT_FIELDS
: {
918 _cleanup_strv_free_
char **v
= NULL
;
920 v
= strv_split(optarg
, ",");
924 if (!arg_output_fields
)
925 arg_output_fields
= TAKE_PTR(v
);
927 r
= strv_extend_strv(&arg_output_fields
, v
, true);
938 assert_not_reached("Unhandled option");
941 if (arg_follow
&& !arg_no_tail
&& !arg_since
&& arg_lines
== ARG_LINES_DEFAULT
)
944 if (!!arg_directory
+ !!arg_file
+ !!arg_machine
+ !!arg_root
> 1) {
945 log_error("Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root.");
949 if (arg_since_set
&& arg_until_set
&& arg_since
> arg_until
) {
950 log_error("--since= must be before --until=.");
954 if (!!arg_cursor
+ !!arg_after_cursor
+ !!arg_since_set
> 1) {
955 log_error("Please specify only one of --since=, --cursor=, and --after-cursor.");
959 if (arg_follow
&& arg_reverse
) {
960 log_error("Please specify either --reverse= or --follow=, not both.");
964 if (!IN_SET(arg_action
, ACTION_SHOW
, ACTION_DUMP_CATALOG
, ACTION_LIST_CATALOG
) && optind
< argc
) {
965 log_error("Extraneous arguments starting with '%s'", argv
[optind
]);
969 if ((arg_boot
|| arg_action
== ACTION_LIST_BOOTS
) && arg_merge
) {
970 log_error("Using --boot or --list-boots with --merge is not supported.");
974 if (!strv_isempty(arg_system_units
) && arg_journal_type
== SD_JOURNAL_CURRENT_USER
) {
975 /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user
976 * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice
977 * to users, and automatically turn --unit= into --user-unit= if combined with --user. */
978 r
= strv_extend_strv(&arg_user_units
, arg_system_units
, true);
982 arg_system_units
= strv_free(arg_system_units
);
990 if (arg_case_sensitive
>= 0)
991 flags
= !arg_case_sensitive
* PCRE2_CASELESS
;
993 _cleanup_(pcre2_match_data_freep
) pcre2_match_data
*md
= NULL
;
995 _cleanup_(pcre2_code_freep
) pcre2_code
*cs
= NULL
;
997 md
= pcre2_match_data_create(1, NULL
);
1001 r
= pattern_compile("[[:upper:]]", 0, &cs
);
1005 r
= pcre2_match(cs
, (PCRE2_SPTR8
) arg_pattern
, PCRE2_ZERO_TERMINATED
, 0, 0, md
, NULL
);
1008 flags
= !has_case
* PCRE2_CASELESS
;
1011 log_debug("Doing case %s matching based on %s",
1012 flags
& PCRE2_CASELESS
? "insensitive" : "sensitive",
1013 arg_case_sensitive
>= 0 ? "request" : "pattern casing");
1015 r
= pattern_compile(arg_pattern
, flags
, &arg_compiled_pattern
);
1024 static int generate_new_id128(void) {
1029 r
= sd_id128_randomize(&id
);
1031 return log_error_errno(r
, "Failed to generate ID: %m");
1033 printf("As string:\n"
1034 SD_ID128_FORMAT_STR
"\n\n"
1036 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
1037 "As man:sd-id128(3) macro:\n"
1038 "#define MESSAGE_XYZ SD_ID128_MAKE(",
1039 SD_ID128_FORMAT_VAL(id
),
1040 SD_ID128_FORMAT_VAL(id
));
1041 for (i
= 0; i
< 16; i
++)
1042 printf("%02x%s", id
.bytes
[i
], i
!= 15 ? "," : "");
1043 fputs(")\n\n", stdout
);
1045 printf("As Python constant:\n"
1047 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR
"')\n",
1048 SD_ID128_FORMAT_VAL(id
));
1053 static int add_matches(sd_journal
*j
, char **args
) {
1055 bool have_term
= false;
1059 STRV_FOREACH(i
, args
) {
1062 if (streq(*i
, "+")) {
1065 r
= sd_journal_add_disjunction(j
);
1068 } else if (path_is_absolute(*i
)) {
1069 _cleanup_free_
char *p
= NULL
, *t
= NULL
, *t2
= NULL
, *interpreter
= NULL
;
1072 r
= chase_symlinks(*i
, NULL
, CHASE_TRAIL_SLASH
, &p
);
1074 return log_error_errno(r
, "Couldn't canonicalize path: %m");
1076 if (lstat(p
, &st
) < 0)
1077 return log_error_errno(errno
, "Couldn't stat file: %m");
1079 if (S_ISREG(st
.st_mode
) && (0111 & st
.st_mode
)) {
1080 if (executable_is_script(p
, &interpreter
) > 0) {
1081 _cleanup_free_
char *comm
;
1083 comm
= strndup(basename(p
), 15);
1087 t
= strappend("_COMM=", comm
);
1091 /* Append _EXE only if the interpreter is not a link.
1092 Otherwise, it might be outdated often. */
1093 if (lstat(interpreter
, &st
) == 0 && !S_ISLNK(st
.st_mode
)) {
1094 t2
= strappend("_EXE=", interpreter
);
1099 t
= strappend("_EXE=", p
);
1104 r
= sd_journal_add_match(j
, t
, 0);
1107 r
= sd_journal_add_match(j
, t2
, 0);
1109 } else if (S_ISCHR(st
.st_mode
) || S_ISBLK(st
.st_mode
)) {
1110 r
= add_matches_for_device(j
, p
);
1114 log_error("File is neither a device node, nor regular file, nor executable: %s", *i
);
1120 r
= sd_journal_add_match(j
, *i
, 0);
1125 return log_error_errno(r
, "Failed to add match '%s': %m", *i
);
1128 if (!strv_isempty(args
) && !have_term
) {
1129 log_error("\"+\" can only be used between terms");
1136 static void boot_id_free_all(BootId
*l
) {
1140 LIST_REMOVE(boot_list
, l
, i
);
1145 static int discover_next_boot(sd_journal
*j
,
1146 sd_id128_t previous_boot_id
,
1150 _cleanup_free_ BootId
*next_boot
= NULL
;
1151 char match
[9+32+1] = "_BOOT_ID=";
1158 /* We expect the journal to be on the last position of a boot
1159 * (in relation to the direction we are going), so that the next
1160 * invocation of sd_journal_next/previous will be from a different
1161 * boot. We then collect any information we desire and then jump
1162 * to the last location of the new boot by using a _BOOT_ID match
1163 * coming from the other journal direction. */
1165 /* Make sure we aren't restricted by any _BOOT_ID matches, so that
1166 * we can actually advance to a *different* boot. */
1167 sd_journal_flush_matches(j
);
1171 r
= sd_journal_previous(j
);
1173 r
= sd_journal_next(j
);
1177 return 0; /* End of journal, yay. */
1179 r
= sd_journal_get_monotonic_usec(j
, NULL
, &boot_id
);
1183 /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
1184 * normally, this will only require a single iteration, as we seeked to the last entry of the previous
1185 * boot entry already. However, it might happen that the per-journal-field entry arrays are less
1186 * complete than the main entry array, and hence might reference an entry that's not actually the last
1187 * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
1188 * speed things up, but let's not trust that it is complete, and hence, manually advance as
1191 } while (sd_id128_equal(boot_id
, previous_boot_id
));
1193 next_boot
= new0(BootId
, 1);
1197 next_boot
->id
= boot_id
;
1199 r
= sd_journal_get_realtime_usec(j
, &next_boot
->first
);
1203 /* Now seek to the last occurrence of this boot ID. */
1204 sd_id128_to_string(next_boot
->id
, match
+ 9);
1205 r
= sd_journal_add_match(j
, match
, sizeof(match
) - 1);
1210 r
= sd_journal_seek_head(j
);
1212 r
= sd_journal_seek_tail(j
);
1217 r
= sd_journal_next(j
);
1219 r
= sd_journal_previous(j
);
1223 log_debug("Whoopsie! We found a boot ID but can't read its last entry.");
1224 return -ENODATA
; /* This shouldn't happen. We just came from this very boot ID. */
1227 r
= sd_journal_get_realtime_usec(j
, &next_boot
->last
);
1231 *ret
= TAKE_PTR(next_boot
);
1236 static int get_boots(
1239 sd_id128_t
*boot_id
,
1244 BootId
*head
= NULL
, *tail
= NULL
, *id
;
1245 const bool advance_older
= boot_id
&& offset
<= 0;
1246 sd_id128_t previous_boot_id
;
1250 /* Adjust for the asymmetry that offset 0 is
1251 * the last (and current) boot, while 1 is considered the
1252 * (chronological) first boot in the journal. */
1253 skip_once
= boot_id
&& sd_id128_is_null(*boot_id
) && offset
<= 0;
1255 /* Advance to the earliest/latest occurrence of our reference
1256 * boot ID (taking our lookup direction into account), so that
1257 * discover_next_boot() can do its job.
1258 * If no reference is given, the journal head/tail will do,
1259 * they're "virtual" boots after all. */
1260 if (boot_id
&& !sd_id128_is_null(*boot_id
)) {
1261 char match
[9+32+1] = "_BOOT_ID=";
1263 sd_journal_flush_matches(j
);
1265 sd_id128_to_string(*boot_id
, match
+ 9);
1266 r
= sd_journal_add_match(j
, match
, sizeof(match
) - 1);
1271 r
= sd_journal_seek_head(j
); /* seek to oldest */
1273 r
= sd_journal_seek_tail(j
); /* seek to newest */
1278 r
= sd_journal_next(j
); /* read the oldest entry */
1280 r
= sd_journal_previous(j
); /* read the most recently added entry */
1285 else if (offset
== 0) {
1290 /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot
1291 * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at
1292 * the following entry, which must then have an older/newer boot ID */
1296 r
= sd_journal_seek_tail(j
); /* seek to newest */
1298 r
= sd_journal_seek_head(j
); /* seek to oldest */
1302 /* No sd_journal_next()/_previous() here.
1304 * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
1305 * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
1309 previous_boot_id
= SD_ID128_NULL
;
1311 _cleanup_free_ BootId
*current
= NULL
;
1313 r
= discover_next_boot(j
, previous_boot_id
, advance_older
, ¤t
);
1315 boot_id_free_all(head
);
1322 previous_boot_id
= current
->id
;
1326 offset
+= advance_older
? 1 : -1;
1331 *boot_id
= current
->id
;
1335 LIST_FOREACH(boot_list
, id
, head
) {
1336 if (sd_id128_equal(id
->id
, current
->id
)) {
1337 /* boot id already stored, something wrong with the journal files */
1338 /* exiting as otherwise this problem would cause forever loop */
1342 LIST_INSERT_AFTER(boot_list
, head
, tail
, current
);
1343 tail
= TAKE_PTR(current
);
1352 sd_journal_flush_matches(j
);
1357 static int list_boots(sd_journal
*j
) {
1359 BootId
*id
, *all_ids
;
1363 count
= get_boots(j
, &all_ids
, NULL
, 0);
1365 return log_error_errno(count
, "Failed to determine boots: %m");
1369 (void) pager_open(arg_no_pager
, arg_pager_end
);
1371 /* numbers are one less, but we need an extra char for the sign */
1372 w
= DECIMAL_STR_WIDTH(count
- 1) + 1;
1375 LIST_FOREACH(boot_list
, id
, all_ids
) {
1376 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
];
1378 printf("% *i " SD_ID128_FORMAT_STR
" %s—%s\n",
1380 SD_ID128_FORMAT_VAL(id
->id
),
1381 format_timestamp_maybe_utc(a
, sizeof(a
), id
->first
),
1382 format_timestamp_maybe_utc(b
, sizeof(b
), id
->last
));
1386 boot_id_free_all(all_ids
);
1391 static int add_boot(sd_journal
*j
) {
1392 char match
[9+32+1] = "_BOOT_ID=";
1401 /* Take a shortcut and use the current boot_id, which we can do very quickly.
1402 * We can do this only when we logs are coming from the current machine,
1403 * so take the slow path if log location is specified. */
1404 if (arg_boot_offset
== 0 && sd_id128_is_null(arg_boot_id
) &&
1405 !arg_directory
&& !arg_file
&& !arg_root
)
1407 return add_match_this_boot(j
, arg_machine
);
1409 boot_id
= arg_boot_id
;
1410 r
= get_boots(j
, NULL
, &boot_id
, arg_boot_offset
);
1413 const char *reason
= (r
== 0) ? "No such boot ID in journal" : strerror(-r
);
1415 if (sd_id128_is_null(arg_boot_id
))
1416 log_error("Data from the specified boot (%+i) is not available: %s",
1417 arg_boot_offset
, reason
);
1419 log_error("Data from the specified boot ("SD_ID128_FORMAT_STR
") is not available: %s",
1420 SD_ID128_FORMAT_VAL(arg_boot_id
), reason
);
1422 return r
== 0 ? -ENODATA
: r
;
1425 sd_id128_to_string(boot_id
, match
+ 9);
1427 r
= sd_journal_add_match(j
, match
, sizeof(match
) - 1);
1429 return log_error_errno(r
, "Failed to add match: %m");
1431 r
= sd_journal_add_conjunction(j
);
1433 return log_error_errno(r
, "Failed to add conjunction: %m");
1438 static int add_dmesg(sd_journal
*j
) {
1445 r
= sd_journal_add_match(j
, "_TRANSPORT=kernel",
1446 STRLEN("_TRANSPORT=kernel"));
1448 return log_error_errno(r
, "Failed to add match: %m");
1450 r
= sd_journal_add_conjunction(j
);
1452 return log_error_errno(r
, "Failed to add conjunction: %m");
1457 static int get_possible_units(
1463 _cleanup_set_free_free_ Set
*found
;
1467 found
= set_new(&string_hash_ops
);
1471 NULSTR_FOREACH(field
, fields
) {
1475 r
= sd_journal_query_unique(j
, field
);
1479 SD_JOURNAL_FOREACH_UNIQUE(j
, data
, size
) {
1480 char **pattern
, *eq
;
1482 _cleanup_free_
char *u
= NULL
;
1484 eq
= memchr(data
, '=', size
);
1486 prefix
= eq
- (char*) data
+ 1;
1490 u
= strndup((char*) data
+ prefix
, size
- prefix
);
1494 STRV_FOREACH(pattern
, patterns
)
1495 if (fnmatch(*pattern
, u
, FNM_NOESCAPE
) == 0) {
1496 log_debug("Matched %s with pattern %s=%s", u
, field
, *pattern
);
1498 r
= set_consume(found
, u
);
1500 if (r
< 0 && r
!= -EEXIST
)
1508 *units
= TAKE_PTR(found
);
1513 /* This list is supposed to return the superset of unit names
1514 * possibly matched by rules added with add_matches_for_unit... */
1515 #define SYSTEM_UNITS \
1519 "OBJECT_SYSTEMD_UNIT\0" \
1522 /* ... and add_matches_for_user_unit */
1523 #define USER_UNITS \
1524 "_SYSTEMD_USER_UNIT\0" \
1526 "COREDUMP_USER_UNIT\0" \
1527 "OBJECT_SYSTEMD_USER_UNIT\0"
1529 static int add_units(sd_journal
*j
) {
1530 _cleanup_strv_free_
char **patterns
= NULL
;
1536 STRV_FOREACH(i
, arg_system_units
) {
1537 _cleanup_free_
char *u
= NULL
;
1539 r
= unit_name_mangle(*i
, UNIT_NAME_MANGLE_GLOB
| (arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
), &u
);
1543 if (string_is_glob(u
)) {
1544 r
= strv_push(&patterns
, u
);
1549 r
= add_matches_for_unit(j
, u
);
1552 r
= sd_journal_add_disjunction(j
);
1559 if (!strv_isempty(patterns
)) {
1560 _cleanup_set_free_free_ Set
*units
= NULL
;
1564 r
= get_possible_units(j
, SYSTEM_UNITS
, patterns
, &units
);
1568 SET_FOREACH(u
, units
, it
) {
1569 r
= add_matches_for_unit(j
, u
);
1572 r
= sd_journal_add_disjunction(j
);
1579 patterns
= strv_free(patterns
);
1581 STRV_FOREACH(i
, arg_user_units
) {
1582 _cleanup_free_
char *u
= NULL
;
1584 r
= unit_name_mangle(*i
, UNIT_NAME_MANGLE_GLOB
| (arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
), &u
);
1588 if (string_is_glob(u
)) {
1589 r
= strv_push(&patterns
, u
);
1594 r
= add_matches_for_user_unit(j
, u
, getuid());
1597 r
= sd_journal_add_disjunction(j
);
1604 if (!strv_isempty(patterns
)) {
1605 _cleanup_set_free_free_ Set
*units
= NULL
;
1609 r
= get_possible_units(j
, USER_UNITS
, patterns
, &units
);
1613 SET_FOREACH(u
, units
, it
) {
1614 r
= add_matches_for_user_unit(j
, u
, getuid());
1617 r
= sd_journal_add_disjunction(j
);
1624 /* Complain if the user request matches but nothing whatsoever was
1625 * found, since otherwise everything would be matched. */
1626 if (!(strv_isempty(arg_system_units
) && strv_isempty(arg_user_units
)) && count
== 0)
1629 r
= sd_journal_add_conjunction(j
);
1636 static int add_priorities(sd_journal
*j
) {
1637 char match
[] = "PRIORITY=0";
1641 if (arg_priorities
== 0xFF)
1644 for (i
= LOG_EMERG
; i
<= LOG_DEBUG
; i
++)
1645 if (arg_priorities
& (1 << i
)) {
1646 match
[sizeof(match
)-2] = '0' + i
;
1648 r
= sd_journal_add_match(j
, match
, strlen(match
));
1650 return log_error_errno(r
, "Failed to add match: %m");
1653 r
= sd_journal_add_conjunction(j
);
1655 return log_error_errno(r
, "Failed to add conjunction: %m");
1661 static int add_syslog_identifier(sd_journal
*j
) {
1667 STRV_FOREACH(i
, arg_syslog_identifier
) {
1670 u
= strjoina("SYSLOG_IDENTIFIER=", *i
);
1671 r
= sd_journal_add_match(j
, u
, 0);
1674 r
= sd_journal_add_disjunction(j
);
1679 r
= sd_journal_add_conjunction(j
);
1686 static int setup_keys(void) {
1688 size_t mpk_size
, seed_size
, state_size
, i
;
1689 uint8_t *mpk
, *seed
, *state
;
1691 sd_id128_t machine
, boot
;
1692 char *p
= NULL
, *k
= NULL
;
1697 r
= stat("/var/log/journal", &st
);
1698 if (r
< 0 && !IN_SET(errno
, ENOENT
, ENOTDIR
))
1699 return log_error_errno(errno
, "stat(\"%s\") failed: %m", "/var/log/journal");
1701 if (r
< 0 || !S_ISDIR(st
.st_mode
)) {
1702 log_error("%s is not a directory, must be using persistent logging for FSS.",
1703 "/var/log/journal");
1704 return r
< 0 ? -errno
: -ENOTDIR
;
1707 r
= sd_id128_get_machine(&machine
);
1709 return log_error_errno(r
, "Failed to get machine ID: %m");
1711 r
= sd_id128_get_boot(&boot
);
1713 return log_error_errno(r
, "Failed to get boot ID: %m");
1715 if (asprintf(&p
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
1716 SD_ID128_FORMAT_VAL(machine
)) < 0)
1721 if (r
< 0 && errno
!= ENOENT
) {
1722 r
= log_error_errno(errno
, "unlink(\"%s\") failed: %m", p
);
1725 } else if (access(p
, F_OK
) >= 0) {
1726 log_error("Sealing key file %s exists already. Use --force to recreate.", p
);
1731 if (asprintf(&k
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss.tmp.XXXXXX",
1732 SD_ID128_FORMAT_VAL(machine
)) < 0) {
1737 mpk_size
= FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR
);
1738 mpk
= alloca(mpk_size
);
1740 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
1741 seed
= alloca(seed_size
);
1743 state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
1744 state
= alloca(state_size
);
1746 fd
= open("/dev/random", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1748 r
= log_error_errno(errno
, "Failed to open /dev/random: %m");
1752 log_info("Generating seed...");
1753 r
= loop_read_exact(fd
, seed
, seed_size
, true);
1755 log_error_errno(r
, "Failed to read random seed: %m");
1759 log_info("Generating key pair...");
1760 FSPRG_GenMK(NULL
, mpk
, seed
, seed_size
, FSPRG_RECOMMENDED_SECPAR
);
1762 log_info("Generating sealing key...");
1763 FSPRG_GenState0(state
, mpk
, seed
, seed_size
);
1765 assert(arg_interval
> 0);
1767 n
= now(CLOCK_REALTIME
);
1771 fd
= mkostemp_safe(k
);
1773 r
= log_error_errno(fd
, "Failed to open %s: %m", k
);
1777 /* Enable secure remove, exclusion from dump, synchronous
1778 * writing and in-place updating */
1779 r
= chattr_fd(fd
, FS_SECRM_FL
|FS_NODUMP_FL
|FS_SYNC_FL
|FS_NOCOW_FL
, FS_SECRM_FL
|FS_NODUMP_FL
|FS_SYNC_FL
|FS_NOCOW_FL
);
1781 log_warning_errno(r
, "Failed to set file attributes: %m");
1784 memcpy(h
.signature
, "KSHHRHLP", 8);
1785 h
.machine_id
= machine
;
1787 h
.header_size
= htole64(sizeof(h
));
1788 h
.start_usec
= htole64(n
* arg_interval
);
1789 h
.interval_usec
= htole64(arg_interval
);
1790 h
.fsprg_secpar
= htole16(FSPRG_RECOMMENDED_SECPAR
);
1791 h
.fsprg_state_size
= htole64(state_size
);
1793 r
= loop_write(fd
, &h
, sizeof(h
), false);
1795 log_error_errno(r
, "Failed to write header: %m");
1799 r
= loop_write(fd
, state
, state_size
, false);
1801 log_error_errno(r
, "Failed to write state: %m");
1805 if (link(k
, p
) < 0) {
1806 r
= log_error_errno(errno
, "Failed to link file: %m");
1813 "The new key pair has been generated. The %ssecret sealing key%s has been written to\n"
1814 "the following local file. This key file is automatically updated when the\n"
1815 "sealing key is advanced. It should not be used on multiple hosts.\n"
1819 "Please write down the following %ssecret verification key%s. It should be stored\n"
1820 "at a safe location and should not be saved locally on disk.\n"
1822 ansi_highlight(), ansi_normal(),
1824 ansi_highlight(), ansi_normal(),
1825 ansi_highlight_red());
1828 for (i
= 0; i
< seed_size
; i
++) {
1829 if (i
> 0 && i
% 3 == 0)
1831 printf("%02x", ((uint8_t*) seed
)[i
]);
1834 printf("/%llx-%llx\n", (unsigned long long) n
, (unsigned long long) arg_interval
);
1837 char tsb
[FORMAT_TIMESPAN_MAX
], *hn
;
1841 "The sealing key is automatically changed every %s.\n",
1843 format_timespan(tsb
, sizeof(tsb
), arg_interval
, 0));
1845 hn
= gethostname_malloc();
1848 hostname_cleanup(hn
);
1849 fprintf(stderr
, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR
".\n", hn
, SD_ID128_FORMAT_VAL(machine
));
1851 fprintf(stderr
, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR
".\n", SD_ID128_FORMAT_VAL(machine
));
1854 /* If this is not an UTF-8 system don't print any QR codes */
1855 if (is_locale_utf8()) {
1856 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr
);
1857 print_qr_code(stderr
, seed
, seed_size
, n
, arg_interval
, hn
, machine
);
1877 log_error("Forward-secure sealing not available.");
1882 static int verify(sd_journal
*j
) {
1889 log_show_color(true);
1891 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
1893 usec_t first
= 0, validated
= 0, last
= 0;
1896 if (!arg_verify_key
&& JOURNAL_HEADER_SEALED(f
->header
))
1897 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f
->path
);
1900 k
= journal_file_verify(f
, arg_verify_key
, &first
, &validated
, &last
, true);
1902 /* If the key was invalid give up right-away. */
1905 log_warning_errno(k
, "FAIL: %s (%m)", f
->path
);
1908 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
], c
[FORMAT_TIMESPAN_MAX
];
1909 log_info("PASS: %s", f
->path
);
1911 if (arg_verify_key
&& JOURNAL_HEADER_SEALED(f
->header
)) {
1912 if (validated
> 0) {
1913 log_info("=> Validated from %s to %s, final %s entries not sealed.",
1914 format_timestamp_maybe_utc(a
, sizeof(a
), first
),
1915 format_timestamp_maybe_utc(b
, sizeof(b
), validated
),
1916 format_timespan(c
, sizeof(c
), last
> validated
? last
- validated
: 0, 0));
1917 } else if (last
> 0)
1918 log_info("=> No sealing yet, %s of entries not sealed.",
1919 format_timespan(c
, sizeof(c
), last
- first
, 0));
1921 log_info("=> No sealing yet, no entries in file.");
1929 static int flush_to_var(void) {
1930 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1931 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1932 _cleanup_close_
int watch_fd
= -1;
1936 log_error("--flush is not supported in conjunction with --machine=.");
1941 if (access("/run/systemd/journal/flushed", F_OK
) >= 0)
1944 /* OK, let's actually do the full logic, send SIGUSR1 to the
1945 * daemon and set up inotify to wait for the flushed file to appear */
1946 r
= bus_connect_system_systemd(&bus
);
1948 return log_error_errno(r
, "Failed to get D-Bus connection: %m");
1950 r
= sd_bus_call_method(
1952 "org.freedesktop.systemd1",
1953 "/org/freedesktop/systemd1",
1954 "org.freedesktop.systemd1.Manager",
1958 "ssi", "systemd-journald.service", "main", SIGUSR1
);
1960 return log_error_errno(r
, "Failed to kill journal service: %s", bus_error_message(&error
, r
));
1962 mkdir_p("/run/systemd/journal", 0755);
1964 watch_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1966 return log_error_errno(errno
, "Failed to create inotify watch: %m");
1968 r
= inotify_add_watch(watch_fd
, "/run/systemd/journal", IN_CREATE
|IN_DONT_FOLLOW
|IN_ONLYDIR
);
1970 return log_error_errno(errno
, "Failed to watch journal directory: %m");
1973 if (access("/run/systemd/journal/flushed", F_OK
) >= 0)
1976 if (errno
!= ENOENT
)
1977 return log_error_errno(errno
, "Failed to check for existence of /run/systemd/journal/flushed: %m");
1979 r
= fd_wait_for_event(watch_fd
, POLLIN
, USEC_INFINITY
);
1981 return log_error_errno(r
, "Failed to wait for event: %m");
1983 r
= flush_fd(watch_fd
);
1985 return log_error_errno(r
, "Failed to flush inotify events: %m");
1991 static int send_signal_and_wait(int sig
, const char *watch_path
) {
1992 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1993 _cleanup_close_
int watch_fd
= -1;
1998 log_error("--sync and --rotate are not supported in conjunction with --machine=.");
2002 start
= now(CLOCK_MONOTONIC
);
2004 /* This call sends the specified signal to journald, and waits
2005 * for acknowledgment by watching the mtime of the specified
2006 * flag file. This is used to trigger syncing or rotation and
2007 * then wait for the operation to complete. */
2012 /* See if a sync happened by now. */
2013 r
= read_timestamp_file(watch_path
, &tstamp
);
2014 if (r
< 0 && r
!= -ENOENT
)
2015 return log_error_errno(errno
, "Failed to read %s: %m", watch_path
);
2016 if (r
>= 0 && tstamp
>= start
)
2019 /* Let's ask for a sync, but only once. */
2021 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2023 r
= bus_connect_system_systemd(&bus
);
2025 return log_error_errno(r
, "Failed to get D-Bus connection: %m");
2027 r
= sd_bus_call_method(
2029 "org.freedesktop.systemd1",
2030 "/org/freedesktop/systemd1",
2031 "org.freedesktop.systemd1.Manager",
2035 "ssi", "systemd-journald.service", "main", sig
);
2037 return log_error_errno(r
, "Failed to kill journal service: %s", bus_error_message(&error
, r
));
2042 /* Let's install the inotify watch, if we didn't do that yet. */
2045 mkdir_p("/run/systemd/journal", 0755);
2047 watch_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
2049 return log_error_errno(errno
, "Failed to create inotify watch: %m");
2051 r
= inotify_add_watch(watch_fd
, "/run/systemd/journal", IN_MOVED_TO
|IN_DONT_FOLLOW
|IN_ONLYDIR
);
2053 return log_error_errno(errno
, "Failed to watch journal directory: %m");
2055 /* Recheck the flag file immediately, so that we don't miss any event since the last check. */
2059 /* OK, all preparatory steps done, let's wait until
2060 * inotify reports an event. */
2062 r
= fd_wait_for_event(watch_fd
, POLLIN
, USEC_INFINITY
);
2064 return log_error_errno(r
, "Failed to wait for event: %m");
2066 r
= flush_fd(watch_fd
);
2068 return log_error_errno(r
, "Failed to flush inotify events: %m");
2074 static int rotate(void) {
2075 return send_signal_and_wait(SIGUSR2
, "/run/systemd/journal/rotated");
2078 static int sync_journal(void) {
2079 return send_signal_and_wait(SIGRTMIN
+1, "/run/systemd/journal/synced");
2082 int main(int argc
, char *argv
[]) {
2084 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2085 bool need_seek
= false;
2086 sd_id128_t previous_boot_id
;
2087 bool previous_boot_id_valid
= false, first_line
= true;
2089 bool ellipsized
= false;
2091 setlocale(LC_ALL
, "");
2092 log_parse_environment();
2095 r
= parse_argv(argc
, argv
);
2099 signal(SIGWINCH
, columns_lines_cache_reset
);
2102 /* Increase max number of open files to 16K if we can, we
2103 * might needs this when browsing journal files, which might
2104 * be split up into many files. */
2105 setrlimit_closest(RLIMIT_NOFILE
, &RLIMIT_MAKE_CONST(16384));
2107 switch (arg_action
) {
2109 case ACTION_NEW_ID128
:
2110 r
= generate_new_id128();
2113 case ACTION_SETUP_KEYS
:
2117 case ACTION_LIST_CATALOG
:
2118 case ACTION_DUMP_CATALOG
:
2119 case ACTION_UPDATE_CATALOG
: {
2120 _cleanup_free_
char *database
;
2122 database
= path_join(arg_root
, CATALOG_DATABASE
, NULL
);
2128 if (arg_action
== ACTION_UPDATE_CATALOG
) {
2129 r
= catalog_update(database
, arg_root
, catalog_file_dirs
);
2131 log_error_errno(r
, "Failed to list catalog: %m");
2133 bool oneline
= arg_action
== ACTION_LIST_CATALOG
;
2135 (void) pager_open(arg_no_pager
, arg_pager_end
);
2138 r
= catalog_list_items(stdout
, database
, oneline
, argv
+ optind
);
2140 r
= catalog_list(stdout
, database
, oneline
);
2142 log_error_errno(r
, "Failed to list catalog: %m");
2161 case ACTION_PRINT_HEADER
:
2163 case ACTION_DISK_USAGE
:
2164 case ACTION_LIST_BOOTS
:
2166 case ACTION_LIST_FIELDS
:
2167 case ACTION_LIST_FIELD_NAMES
:
2168 /* These ones require access to the journal files, continue below. */
2172 assert_not_reached("Unknown action");
2176 r
= sd_journal_open_directory(&j
, arg_directory
, arg_journal_type
);
2178 r
= sd_journal_open_directory(&j
, arg_root
, arg_journal_type
| SD_JOURNAL_OS_ROOT
);
2179 else if (arg_file_stdin
) {
2180 int ifd
= STDIN_FILENO
;
2181 r
= sd_journal_open_files_fd(&j
, &ifd
, 1, 0);
2182 } else if (arg_file
)
2183 r
= sd_journal_open_files(&j
, (const char**) arg_file
, 0);
2184 else if (arg_machine
) {
2185 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2186 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2187 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2190 if (geteuid() != 0) {
2191 /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
2192 * the container, thus we need root privileges to override them. */
2193 log_error("Using the --machine= switch requires root privileges.");
2198 r
= sd_bus_open_system(&bus
);
2200 log_error_errno(r
, "Failed to open system bus: %m");
2204 r
= sd_bus_call_method(
2206 "org.freedesktop.machine1",
2207 "/org/freedesktop/machine1",
2208 "org.freedesktop.machine1.Manager",
2209 "OpenMachineRootDirectory",
2214 log_error_errno(r
, "Failed to open root directory: %s", bus_error_message(&error
, r
));
2218 r
= sd_bus_message_read(reply
, "h", &fd
);
2220 bus_log_parse_error(r
);
2224 fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2226 r
= log_error_errno(errno
, "Failed to duplicate file descriptor: %m");
2230 r
= sd_journal_open_directory_fd(&j
, fd
, SD_JOURNAL_OS_ROOT
);
2234 r
= sd_journal_open(&j
, !arg_merge
*SD_JOURNAL_LOCAL_ONLY
+ arg_journal_type
);
2236 log_error_errno(r
, "Failed to open %s: %m", arg_directory
?: arg_file
? "files" : "journal");
2240 r
= journal_access_check_and_warn(j
, arg_quiet
,
2241 !(arg_journal_type
== SD_JOURNAL_CURRENT_USER
|| arg_user_units
));
2245 switch (arg_action
) {
2247 case ACTION_NEW_ID128
:
2248 case ACTION_SETUP_KEYS
:
2249 case ACTION_LIST_CATALOG
:
2250 case ACTION_DUMP_CATALOG
:
2251 case ACTION_UPDATE_CATALOG
:
2255 assert_not_reached("Unexpected action.");
2257 case ACTION_PRINT_HEADER
:
2258 journal_print_header(j
);
2266 case ACTION_DISK_USAGE
: {
2268 char sbytes
[FORMAT_BYTES_MAX
];
2270 r
= sd_journal_get_usage(j
, &bytes
);
2274 printf("Archived and active journals take up %s in the file system.\n",
2275 format_bytes(sbytes
, sizeof(sbytes
), bytes
));
2279 case ACTION_LIST_BOOTS
:
2283 case ACTION_VACUUM
: {
2287 HASHMAP_FOREACH(d
, j
->directories_by_path
, i
) {
2293 q
= journal_directory_vacuum(d
->path
, arg_vacuum_size
, arg_vacuum_n_files
, arg_vacuum_time
, NULL
, !arg_quiet
);
2295 log_error_errno(q
, "Failed to vacuum %s: %m", d
->path
);
2303 case ACTION_LIST_FIELD_NAMES
: {
2306 SD_JOURNAL_FOREACH_FIELD(j
, field
) {
2307 printf("%s\n", field
);
2316 case ACTION_LIST_FIELDS
:
2320 assert_not_reached("Unknown action");
2323 if (arg_boot_offset
!= 0 &&
2324 sd_journal_has_runtime_files(j
) > 0 &&
2325 sd_journal_has_persistent_files(j
) == 0) {
2326 log_info("Specifying boot ID or boot offset has no effect, no persistent journal was found.");
2330 /* add_boot() must be called first!
2331 * It may need to seek the journal to find parent boot IDs. */
2342 log_error_errno(r
, "Failed to add filter for units: %m");
2346 r
= add_syslog_identifier(j
);
2348 log_error_errno(r
, "Failed to add filter for syslog identifiers: %m");
2352 r
= add_priorities(j
);
2356 r
= add_matches(j
, argv
+ optind
);
2360 if (DEBUG_LOGGING
) {
2361 _cleanup_free_
char *filter
;
2363 filter
= journal_make_match_string(j
);
2367 log_debug("Journal filter: %s", filter
);
2370 if (arg_action
== ACTION_LIST_FIELDS
) {
2376 r
= sd_journal_set_data_threshold(j
, 0);
2378 log_error_errno(r
, "Failed to unset data size threshold: %m");
2382 r
= sd_journal_query_unique(j
, arg_field
);
2384 log_error_errno(r
, "Failed to query unique data objects: %m");
2388 SD_JOURNAL_FOREACH_UNIQUE(j
, data
, size
) {
2391 if (arg_lines
>= 0 && n_shown
>= arg_lines
)
2394 eq
= memchr(data
, '=', size
);
2396 printf("%.*s\n", (int) (size
- ((const uint8_t*) eq
- (const uint8_t*) data
+ 1)), (const char*) eq
+ 1);
2398 printf("%.*s\n", (int) size
, (const char*) data
);
2407 /* Opening the fd now means the first sd_journal_wait() will actually wait */
2409 r
= sd_journal_get_fd(j
);
2410 if (r
== -EMEDIUMTYPE
) {
2411 log_error_errno(r
, "The --follow switch is not supported in conjunction with reading from STDIN.");
2415 log_error_errno(r
, "Failed to get journal fd: %m");
2420 if (arg_cursor
|| arg_after_cursor
) {
2421 r
= sd_journal_seek_cursor(j
, arg_cursor
?: arg_after_cursor
);
2423 log_error_errno(r
, "Failed to seek to cursor: %m");
2428 r
= sd_journal_next_skip(j
, 1 + !!arg_after_cursor
);
2430 r
= sd_journal_previous_skip(j
, 1 + !!arg_after_cursor
);
2432 if (arg_after_cursor
&& r
< 2) {
2433 /* We couldn't find the next entry after the cursor. */
2440 } else if (arg_since_set
&& !arg_reverse
) {
2441 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
2443 log_error_errno(r
, "Failed to seek to date: %m");
2446 r
= sd_journal_next(j
);
2448 } else if (arg_until_set
&& arg_reverse
) {
2449 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
2451 log_error_errno(r
, "Failed to seek to date: %m");
2454 r
= sd_journal_previous(j
);
2456 } else if (arg_lines
>= 0) {
2457 r
= sd_journal_seek_tail(j
);
2459 log_error_errno(r
, "Failed to seek to tail: %m");
2463 r
= sd_journal_previous_skip(j
, arg_lines
);
2465 } else if (arg_reverse
) {
2466 r
= sd_journal_seek_tail(j
);
2468 log_error_errno(r
, "Failed to seek to tail: %m");
2472 r
= sd_journal_previous(j
);
2475 r
= sd_journal_seek_head(j
);
2477 log_error_errno(r
, "Failed to seek to head: %m");
2481 r
= sd_journal_next(j
);
2485 log_error_errno(r
, "Failed to iterate through journal: %m");
2492 (void) pager_open(arg_no_pager
, arg_pager_end
);
2494 if (!arg_quiet
&& (arg_lines
!= 0 || arg_follow
)) {
2496 char start_buf
[FORMAT_TIMESTAMP_MAX
], end_buf
[FORMAT_TIMESTAMP_MAX
];
2498 r
= sd_journal_get_cutoff_realtime_usec(j
, &start
, &end
);
2500 log_error_errno(r
, "Failed to get cutoff: %m");
2506 printf("-- Logs begin at %s. --\n",
2507 format_timestamp_maybe_utc(start_buf
, sizeof(start_buf
), start
));
2509 printf("-- Logs begin at %s, end at %s. --\n",
2510 format_timestamp_maybe_utc(start_buf
, sizeof(start_buf
), start
),
2511 format_timestamp_maybe_utc(end_buf
, sizeof(end_buf
), end
));
2516 while (arg_lines
< 0 || n_shown
< arg_lines
|| (arg_follow
&& !first_line
)) {
2518 size_t highlight
[2] = {};
2522 r
= sd_journal_next(j
);
2524 r
= sd_journal_previous(j
);
2526 log_error_errno(r
, "Failed to iterate through journal: %m");
2533 if (arg_until_set
&& !arg_reverse
) {
2536 r
= sd_journal_get_realtime_usec(j
, &usec
);
2538 log_error_errno(r
, "Failed to determine timestamp: %m");
2541 if (usec
> arg_until
)
2545 if (arg_since_set
&& arg_reverse
) {
2548 r
= sd_journal_get_realtime_usec(j
, &usec
);
2550 log_error_errno(r
, "Failed to determine timestamp: %m");
2553 if (usec
< arg_since
)
2557 if (!arg_merge
&& !arg_quiet
) {
2560 r
= sd_journal_get_monotonic_usec(j
, NULL
, &boot_id
);
2562 if (previous_boot_id_valid
&&
2563 !sd_id128_equal(boot_id
, previous_boot_id
))
2564 printf("%s-- Reboot --%s\n",
2565 ansi_highlight(), ansi_normal());
2567 previous_boot_id
= boot_id
;
2568 previous_boot_id_valid
= true;
2573 if (arg_compiled_pattern
) {
2574 _cleanup_(pcre2_match_data_freep
) pcre2_match_data
*md
= NULL
;
2575 const void *message
;
2579 md
= pcre2_match_data_create(1, NULL
);
2583 r
= sd_journal_get_data(j
, "MESSAGE", &message
, &len
);
2590 log_error_errno(r
, "Failed to get MESSAGE field: %m");
2594 assert_se(message
= startswith(message
, "MESSAGE="));
2596 r
= pcre2_match(arg_compiled_pattern
,
2598 len
- strlen("MESSAGE="),
2599 0, /* start at offset 0 in the subject */
2600 0, /* default options */
2603 if (r
== PCRE2_ERROR_NOMATCH
) {
2608 unsigned char buf
[LINE_MAX
];
2611 r2
= pcre2_get_error_message(r
, buf
, sizeof buf
);
2612 log_error("Pattern matching failed: %s",
2613 r2
< 0 ? "unknown error" : (char*) buf
);
2618 ovec
= pcre2_get_ovector_pointer(md
);
2619 highlight
[0] = ovec
[0];
2620 highlight
[1] = ovec
[1];
2625 arg_all
* OUTPUT_SHOW_ALL
|
2626 arg_full
* OUTPUT_FULL_WIDTH
|
2627 colors_enabled() * OUTPUT_COLOR
|
2628 arg_catalog
* OUTPUT_CATALOG
|
2629 arg_utc
* OUTPUT_UTC
|
2630 arg_no_hostname
* OUTPUT_NO_HOSTNAME
;
2632 r
= output_journal(stdout
, j
, arg_output
, 0, flags
,
2633 arg_output_fields
, highlight
, &ellipsized
);
2635 if (r
== -EADDRNOTAVAIL
)
2637 else if (r
< 0 || ferror(stdout
))
2642 /* If journalctl take a long time to process messages, and during that time journal file
2643 * rotation occurs, a journalctl client will keep those rotated files open until it calls
2644 * sd_journal_process(), which typically happens as a result of calling sd_journal_wait() below
2645 * in the "following" case. By periodically calling sd_journal_process() during the processing
2646 * loop we shrink the window of time a client instance has open file descriptors for rotated
2647 * (deleted) journal files. */
2648 if ((n_shown
% PROCESS_INOTIFY_INTERVAL
) == 0) {
2649 r
= sd_journal_process(j
);
2651 log_error_errno(r
, "Failed to process inotify events: %m");
2658 if (n_shown
== 0 && !arg_quiet
)
2659 printf("-- No entries --\n");
2661 if (arg_show_cursor
) {
2662 _cleanup_free_
char *cursor
= NULL
;
2664 r
= sd_journal_get_cursor(j
, &cursor
);
2665 if (r
< 0 && r
!= -EADDRNOTAVAIL
)
2666 log_error_errno(r
, "Failed to get cursor: %m");
2668 printf("-- cursor: %s\n", cursor
);
2675 r
= sd_journal_wait(j
, (uint64_t) -1);
2677 log_error_errno(r
, "Couldn't wait for journal event: %m");
2688 strv_free(arg_file
);
2690 strv_free(arg_syslog_identifier
);
2691 strv_free(arg_system_units
);
2692 strv_free(arg_user_units
);
2693 strv_free(arg_output_fields
);
2696 free(arg_verify_key
);
2699 if (arg_compiled_pattern
)
2700 pcre2_code_free(arg_compiled_pattern
);
2703 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;