]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
boot: a couple of tweaks recommended by Coccinelle
[thirdparty/systemd.git] / src / journal / journalctl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
87d2c1ff 2
3f6fd1ba 3#include <errno.h>
87d2c1ff 4#include <fcntl.h>
ea18a4b5 5#include <fnmatch.h>
3f6fd1ba
LP
6#include <getopt.h>
7#include <linux/fs.h>
3f6fd1ba 8#include <signal.h>
87d2c1ff 9#include <stddef.h>
3fbf9cbb 10#include <stdio.h>
3fbf9cbb 11#include <stdlib.h>
74055aa7 12#include <sys/inotify.h>
3f6fd1ba
LP
13#include <sys/stat.h>
14#include <unistd.h>
87d2c1ff 15
6becf48c
ZJS
16#if HAVE_PCRE2
17# define PCRE2_CODE_UNIT_WIDTH 8
18# include <pcre2.h>
19#endif
20
74055aa7 21#include "sd-bus.h"
8bebb09c 22#include "sd-device.h"
3f6fd1ba
LP
23#include "sd-journal.h"
24
f8eeeaf9 25#include "acl-util.h"
b5efdb8a 26#include "alloc-util.h"
3f6fd1ba
LP
27#include "bus-error.h"
28#include "bus-util.h"
29#include "catalog.h"
f4351959 30#include "chase-symlinks.h"
c8b3094d 31#include "chattr-util.h"
1abaf488 32#include "def.h"
cc171228 33#include "dissect-image.h"
3ffd4af2 34#include "fd-util.h"
68fee104 35#include "fileio.h"
5a1355d8 36#include "format-table.h"
aa892669 37#include "format-util.h"
f4f15635 38#include "fs-util.h"
3f6fd1ba 39#include "fsprg.h"
7d50b32a 40#include "glob-util.h"
3f6fd1ba 41#include "hostname-util.h"
ff7dad48 42#include "id128-print.h"
c004493c 43#include "io-util.h"
7560fffc 44#include "journal-def.h"
3f6fd1ba 45#include "journal-internal.h"
4f37cbd9 46#include "journal-util.h"
dbd2a83f 47#include "journal-vacuum.h"
3f6fd1ba 48#include "journal-verify.h"
8752c575 49#include "locale-util.h"
3f6fd1ba
LP
50#include "log.h"
51#include "logs-show.h"
0a970718 52#include "memory-util.h"
74055aa7 53#include "mkdir.h"
cc171228 54#include "mount-util.h"
c0dfcb31 55#include "mountpoint-util.h"
d8b4d14d 56#include "nulstr-util.h"
3f6fd1ba 57#include "pager.h"
614b022c 58#include "parse-argument.h"
6bedfcbb 59#include "parse-util.h"
3f6fd1ba 60#include "path-util.h"
e30c1d01 61#include "pcre2-util.h"
294bf0c3 62#include "pretty-print.h"
f1b82359 63#include "qrcode-util.h"
9bff1410 64#include "random-util.h"
78f22b97 65#include "rlimit-util.h"
3f6fd1ba
LP
66#include "set.h"
67#include "sigbus.h"
2e64b27a 68#include "static-destruct.h"
9bff1410 69#include "stdio-util.h"
5c828e66 70#include "string-table.h"
3f6fd1ba 71#include "strv.h"
7ccbd1ae 72#include "syslog-util.h"
288a74cc 73#include "terminal-util.h"
e4de7287 74#include "tmpfile-util.h"
3f6fd1ba 75#include "unit-name.h"
b1d4f8e1 76#include "user-util.h"
4f413af2 77#include "varlink.h"
7560fffc 78
baed47c3 79#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
ec316d19
PP
80#define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */
81
97e1cc8b
LP
82enum {
83 /* Special values for arg_lines */
84 ARG_LINES_DEFAULT = -2,
85 ARG_LINES_ALL = -1,
86};
87
df50185b 88static OutputMode arg_output = OUTPUT_SHORT;
5a1355d8 89static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
9fd29044 90static bool arg_utc = false;
72f59706 91static bool arg_follow = false;
2b8f6883 92static bool arg_full = true;
cd4b13e0 93static bool arg_all = false;
0221d68a 94static PagerFlags arg_pager_flags = 0;
97e1cc8b 95static int arg_lines = ARG_LINES_DEFAULT;
e91af489 96static bool arg_no_tail = false;
43673799 97static bool arg_quiet = false;
9e8a535f 98static bool arg_merge = false;
d121b396 99static bool arg_boot = false;
442e2def
LP
100static sd_id128_t arg_boot_id = {};
101static int arg_boot_offset = 0;
99271804 102static bool arg_dmesg = false;
991e274b 103static bool arg_no_hostname = false;
8f14c832 104static const char *arg_cursor = NULL;
d9e15cbd 105static const char *arg_cursor_file = NULL;
248fc619
ZJS
106static const char *arg_after_cursor = NULL;
107static bool arg_show_cursor = false;
a963990f 108static const char *arg_directory = NULL;
8d98da3f 109static char **arg_file = NULL;
5d1ce257 110static bool arg_file_stdin = false;
941e990d 111static int arg_priorities = 0xFF;
196dedd5 112static Set *arg_facilities = NULL;
6bae9b2a 113static char *arg_verify_key = NULL;
349cc4a5 114#if HAVE_GCRYPT
baed47c3 115static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
b8547c10 116static bool arg_force = false;
feb12d3e 117#endif
cfbc22ab
LP
118static usec_t arg_since, arg_until;
119static bool arg_since_set = false, arg_until_set = false;
73083640 120static char **arg_syslog_identifier = NULL;
b9e40524
HH
121static char **arg_system_units = NULL;
122static char **arg_user_units = NULL;
3c1668da 123static const char *arg_field = NULL;
d4205751 124static bool arg_catalog = false;
d89d6c86 125static bool arg_reverse = false;
3f3a438f 126static int arg_journal_type = 0;
6b25db87 127static int arg_namespace_flags = 0;
0f03c2a4 128static char *arg_root = NULL;
cc171228 129static char *arg_image = NULL;
b6741478 130static const char *arg_machine = NULL;
6b25db87 131static const char *arg_namespace = NULL;
8580d1f7
LP
132static uint64_t arg_vacuum_size = 0;
133static uint64_t arg_vacuum_n_files = 0;
134static usec_t arg_vacuum_time = 0;
cc25a67e 135static char **arg_output_fields = NULL;
6becf48c 136#if HAVE_PCRE2
61c5f8a1
ZJS
137static const char *arg_pattern = NULL;
138static pcre2_code *arg_compiled_pattern = NULL;
139static int arg_case_sensitive = -1; /* -1 means be smart */
6becf48c
ZJS
140#endif
141
2e64b27a
DDM
142STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
143STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
144STATIC_DESTRUCTOR_REGISTER(arg_verify_key, freep);
145STATIC_DESTRUCTOR_REGISTER(arg_syslog_identifier, strv_freep);
146STATIC_DESTRUCTOR_REGISTER(arg_system_units, strv_freep);
147STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
148STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
149STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
150STATIC_DESTRUCTOR_REGISTER(arg_output_fields, strv_freep);
151#if HAVE_PCRE2
152STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, sym_pcre2_code_freep);
153#endif
154
7560fffc
LP
155static enum {
156 ACTION_SHOW,
157 ACTION_NEW_ID128,
158 ACTION_PRINT_HEADER,
beec0085 159 ACTION_SETUP_KEYS,
a1a03e30
LP
160 ACTION_VERIFY,
161 ACTION_DISK_USAGE,
d4205751 162 ACTION_LIST_CATALOG,
54b7254c 163 ACTION_DUMP_CATALOG,
f1188074
ZJS
164 ACTION_UPDATE_CATALOG,
165 ACTION_LIST_BOOTS,
74055aa7 166 ACTION_FLUSH,
c0dfcb31 167 ACTION_RELINQUISH_VAR,
a020b3b3 168 ACTION_SYNC,
e3fdfb49 169 ACTION_ROTATE,
dbd2a83f 170 ACTION_VACUUM,
8df64fd0 171 ACTION_ROTATE_AND_VACUUM,
69e714f3
LP
172 ACTION_LIST_FIELDS,
173 ACTION_LIST_FIELD_NAMES,
7560fffc
LP
174} arg_action = ACTION_SHOW;
175
45bc27b6 176typedef struct BootId {
a331b5e6 177 sd_id128_t id;
f1188074
ZJS
178 uint64_t first;
179 uint64_t last;
45bc27b6
LP
180 LIST_FIELDS(struct BootId, boot_list);
181} BootId;
a331b5e6 182
a6214d96 183#if HAVE_PCRE2
a6214d96
LP
184static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
185 int errorcode, r;
186 PCRE2_SIZE erroroffset;
187 pcre2_code *p;
188
9200bb30
LP
189 p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
190 PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
a6214d96
LP
191 if (!p) {
192 unsigned char buf[LINE_MAX];
193
9200bb30 194 r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
a6214d96
LP
195
196 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
197 "Bad pattern \"%s\": %s", pattern,
198 r < 0 ? "unknown error" : (char *)buf);
199 }
200
201 *out = p;
202 return 0;
203}
a6214d96
LP
204#endif
205
795ab08f 206static int add_matches_for_device(sd_journal *j, const char *devpath) {
8bebb09c
YW
207 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
208 sd_device *d = NULL;
795ab08f 209 struct stat st;
e5ca27b7 210 int r;
795ab08f
MS
211
212 assert(j);
213 assert(devpath);
214
d7a0f1f4
FS
215 if (!path_startswith(devpath, "/dev/"))
216 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
217 "Devpath does not start with /dev/");
795ab08f 218
e5ca27b7
LP
219 if (stat(devpath, &st) < 0)
220 return log_error_errno(errno, "Couldn't stat file: %m");
795ab08f 221
a1130022 222 r = sd_device_new_from_stat_rdev(&device, &st);
e5ca27b7 223 if (r < 0)
8bebb09c 224 return log_error_errno(r, "Failed to get device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
795ab08f 225
8bebb09c 226 for (d = device; d; ) {
795ab08f
MS
227 _cleanup_free_ char *match = NULL;
228 const char *subsys, *sysname, *devnode;
8bebb09c 229 sd_device *parent;
795ab08f 230
8bebb09c
YW
231 r = sd_device_get_subsystem(d, &subsys);
232 if (r < 0)
233 goto get_parent;
795ab08f 234
8bebb09c
YW
235 r = sd_device_get_sysname(d, &sysname);
236 if (r < 0)
237 goto get_parent;
795ab08f 238
605405c6 239 match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
795ab08f
MS
240 if (!match)
241 return log_oom();
242
243 r = sd_journal_add_match(j, match, 0);
244 if (r < 0)
245 return log_error_errno(r, "Failed to add match: %m");
246
8bebb09c 247 if (sd_device_get_devname(d, &devnode) >= 0) {
795ab08f
MS
248 _cleanup_free_ char *match1 = NULL;
249
250 r = stat(devnode, &st);
251 if (r < 0)
252 return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode);
253
254 r = asprintf(&match1, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st.st_mode) ? 'b' : 'c', major(st.st_rdev), minor(st.st_rdev));
255 if (r < 0)
256 return log_oom();
257
258 r = sd_journal_add_match(j, match1, 0);
259 if (r < 0)
260 return log_error_errno(r, "Failed to add match: %m");
261 }
262
8bebb09c
YW
263get_parent:
264 if (sd_device_get_parent(d, &parent) < 0)
265 break;
266
267 d = parent;
795ab08f
MS
268 }
269
485fd9a7
MS
270 r = add_match_this_boot(j, arg_machine);
271 if (r < 0)
272 return log_error_errno(r, "Failed to add match for the current boot: %m");
273
795ab08f
MS
274 return 0;
275}
276
5ab99e07
LP
277static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) {
278
279 if (arg_utc)
7b3eb5c9 280 return format_timestamp_style(buf, l, t, TIMESTAMP_UTC);
5ab99e07
LP
281
282 return format_timestamp(buf, l, t);
283}
284
442e2def
LP
285static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) {
286 sd_id128_t id = SD_ID128_NULL;
287 int off = 0, r;
288
48904825 289 if (streq(x, "all")) {
290 *boot_id = SD_ID128_NULL;
291 *offset = 0;
292 return 0;
0d5765f7 293 } else if (strlen(x) >= SD_ID128_STRING_MAX - 1) {
442e2def
LP
294 char *t;
295
2f82562b 296 t = strndupa_safe(x, SD_ID128_STRING_MAX - 1);
442e2def
LP
297 r = sd_id128_from_string(t, &id);
298 if (r >= 0)
0d5765f7 299 x += SD_ID128_STRING_MAX - 1;
442e2def 300
4c701096 301 if (!IN_SET(*x, 0, '-', '+'))
442e2def
LP
302 return -EINVAL;
303
304 if (*x != 0) {
305 r = safe_atoi(x, &off);
306 if (r < 0)
307 return r;
308 }
309 } else {
310 r = safe_atoi(x, &off);
311 if (r < 0)
312 return r;
313 }
314
315 if (boot_id)
316 *boot_id = id;
317
318 if (offset)
319 *offset = off;
320
48904825 321 return 1;
442e2def
LP
322}
323
196dedd5
ZJS
324static int help_facilities(void) {
325 if (!arg_quiet)
326 puts("Available facilities:");
327
328 for (int i = 0; i < LOG_NFACILITIES; i++) {
329 _cleanup_free_ char *t = NULL;
330
331 if (log_facility_unshifted_to_string_alloc(i, &t))
332 return log_oom();
333 puts(t);
334 }
335
336 return 0;
337}
338
37ec0fdd
LP
339static int help(void) {
340 _cleanup_free_ char *link = NULL;
341 int r;
0d43c694 342
384c2c32 343 pager_open(arg_pager_flags);
faf5077f 344
37ec0fdd
LP
345 r = terminal_urlify_man("journalctl", "1", &link);
346 if (r < 0)
347 return log_oom();
348
23d8c560
LP
349 printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
350 "%5$sQuery the journal.%6$s\n\n"
351 "%3$sOptions:%4$s\n"
61c5f8a1
ZJS
352 " --system Show the system journal\n"
353 " --user Show the user journal for the current user\n"
354 " -M --machine=CONTAINER Operate on local container\n"
355 " -S --since=DATE Show entries not older than the specified date\n"
356 " -U --until=DATE Show entries not newer than the specified date\n"
357 " -c --cursor=CURSOR Show entries starting at the specified cursor\n"
358 " --after-cursor=CURSOR Show entries after the specified cursor\n"
359 " --show-cursor Print the cursor after all the entries\n"
d9e15cbd 360 " --cursor-file=FILE Show entries after cursor in FILE and update FILE\n"
61c5f8a1
ZJS
361 " -b --boot[=ID] Show current boot or the specified boot\n"
362 " --list-boots Show terse information about recorded boots\n"
363 " -k --dmesg Show kernel message log from the current boot\n"
364 " -u --unit=UNIT Show logs from the specified unit\n"
365 " --user-unit=UNIT Show logs from the specified user unit\n"
366 " -t --identifier=STRING Show entries with the specified syslog identifier\n"
367 " -p --priority=RANGE Show entries with the specified priority\n"
196dedd5 368 " --facility=FACILITY... Show entries with the specified facilities\n"
fabf4dae 369 " -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
86b52a39 370 " --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
61c5f8a1
ZJS
371 " -e --pager-end Immediately jump to the end in the pager\n"
372 " -f --follow Follow the journal\n"
373 " -n --lines[=INTEGER] Number of journal entries to show\n"
374 " --no-tail Show all lines, even in follow mode\n"
375 " -r --reverse Show the newest entries first\n"
376 " -o --output=STRING Change journal output mode (short, short-precise,\n"
377 " short-iso, short-iso-precise, short-full,\n"
378 " short-monotonic, short-unix, verbose, export,\n"
8e044443
LP
379 " json, json-pretty, json-sse, json-seq, cat,\n"
380 " with-unit)\n"
61c5f8a1
ZJS
381 " --output-fields=LIST Select fields to print in verbose/export/json modes\n"
382 " --utc Express time in Coordinated Universal Time (UTC)\n"
383 " -x --catalog Add message explanations where available\n"
384 " --no-full Ellipsize fields\n"
385 " -a --all Show all fields, including long and unprintable\n"
386 " -q --quiet Do not show info messages and privilege warning\n"
387 " --no-pager Do not pipe output into a pager\n"
388 " --no-hostname Suppress output of hostname field\n"
389 " -m --merge Show entries from all available journals\n"
390 " -D --directory=PATH Show journal files from directory\n"
391 " --file=PATH Show journal file\n"
392 " --root=ROOT Operate on files below a root directory\n"
cc171228 393 " --image=IMAGE Operate on files in filesystem image\n"
6b25db87 394 " --namespace=NAMESPACE Show journal data from specified namespace\n"
61c5f8a1
ZJS
395 " --interval=TIME Time interval for changing the FSS sealing key\n"
396 " --verify-key=KEY Specify FSS verification key\n"
397 " --force Override of the FSS key pair with --setup-keys\n"
23d8c560 398 "\n%3$sCommands:%4$s\n"
61c5f8a1
ZJS
399 " -h --help Show this help text\n"
400 " --version Show package version\n"
401 " -N --fields List all field names currently used\n"
402 " -F --field=FIELD List all values that a specified field takes\n"
403 " --disk-usage Show total disk usage of all journal files\n"
404 " --vacuum-size=BYTES Reduce disk usage below specified size\n"
405 " --vacuum-files=INT Leave only the specified number of journal files\n"
406 " --vacuum-time=TIME Remove journal files older than specified time\n"
407 " --verify Verify journal file consistency\n"
408 " --sync Synchronize unwritten journal messages to disk\n"
c0dfcb31
LP
409 " --relinquish-var Stop logging to disk, log to temporary file system\n"
410 " --smart-relinquish-var Similar, but NOP if log directory is on root mount\n"
61c5f8a1
ZJS
411 " --flush Flush all journal data from /run into /var\n"
412 " --rotate Request immediate rotation of the journal files\n"
413 " --header Show journal header information\n"
414 " --list-catalog Show all message IDs in the catalog\n"
415 " --dump-catalog Show entries in the message catalog\n"
416 " --update-catalog Update the message catalog database\n"
61c5f8a1 417 " --setup-keys Generate a new FSS key pair\n"
bc556335
DDM
418 "\nSee the %2$s for details.\n",
419 program_invocation_short_name,
420 link,
421 ansi_underline(),
422 ansi_normal(),
423 ansi_highlight(),
424 ansi_normal());
37ec0fdd
LP
425
426 return 0;
0d43c694
LP
427}
428
429static int parse_argv(int argc, char *argv[]) {
430
431 enum {
432 ARG_VERSION = 0x100,
e91af489 433 ARG_NO_PAGER,
2b8f6883 434 ARG_NO_FULL,
55ee336c 435 ARG_NO_TAIL,
dca6219e 436 ARG_NEW_ID128,
8453f062 437 ARG_THIS_BOOT,
f1188074 438 ARG_LIST_BOOTS,
3f3a438f
ZJS
439 ARG_USER,
440 ARG_SYSTEM,
13cbf3a5 441 ARG_ROOT,
cc171228 442 ARG_IMAGE,
7560fffc 443 ARG_HEADER,
196dedd5 444 ARG_FACILITY,
beec0085 445 ARG_SETUP_KEYS,
8d98da3f 446 ARG_FILE,
baed47c3 447 ARG_INTERVAL,
4da416aa 448 ARG_VERIFY,
a1a03e30 449 ARG_VERIFY_KEY,
cfbc22ab 450 ARG_DISK_USAGE,
248fc619 451 ARG_AFTER_CURSOR,
d9e15cbd 452 ARG_CURSOR_FILE,
248fc619 453 ARG_SHOW_CURSOR,
ffa7cd15 454 ARG_USER_UNIT,
d4205751 455 ARG_LIST_CATALOG,
54b7254c 456 ARG_DUMP_CATALOG,
3f3a438f 457 ARG_UPDATE_CATALOG,
b8547c10 458 ARG_FORCE,
61c5f8a1 459 ARG_CASE_SENSITIVE,
9fd29044 460 ARG_UTC,
94b65516 461 ARG_SYNC,
74055aa7 462 ARG_FLUSH,
c0dfcb31
LP
463 ARG_RELINQUISH_VAR,
464 ARG_SMART_RELINQUISH_VAR,
e3fdfb49 465 ARG_ROTATE,
dbd2a83f 466 ARG_VACUUM_SIZE,
8580d1f7 467 ARG_VACUUM_FILES,
dbd2a83f 468 ARG_VACUUM_TIME,
991e274b 469 ARG_NO_HOSTNAME,
cc25a67e 470 ARG_OUTPUT_FIELDS,
6b25db87 471 ARG_NAMESPACE,
0d43c694
LP
472 };
473
474 static const struct option options[] = {
c0dfcb31
LP
475 { "help", no_argument, NULL, 'h' },
476 { "version" , no_argument, NULL, ARG_VERSION },
477 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
478 { "pager-end", no_argument, NULL, 'e' },
479 { "follow", no_argument, NULL, 'f' },
480 { "force", no_argument, NULL, ARG_FORCE },
481 { "output", required_argument, NULL, 'o' },
482 { "all", no_argument, NULL, 'a' },
483 { "full", no_argument, NULL, 'l' },
484 { "no-full", no_argument, NULL, ARG_NO_FULL },
485 { "lines", optional_argument, NULL, 'n' },
486 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
487 { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */
488 { "quiet", no_argument, NULL, 'q' },
489 { "merge", no_argument, NULL, 'm' },
490 { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */
491 { "boot", optional_argument, NULL, 'b' },
492 { "list-boots", no_argument, NULL, ARG_LIST_BOOTS },
493 { "dmesg", no_argument, NULL, 'k' },
494 { "system", no_argument, NULL, ARG_SYSTEM },
495 { "user", no_argument, NULL, ARG_USER },
496 { "directory", required_argument, NULL, 'D' },
497 { "file", required_argument, NULL, ARG_FILE },
498 { "root", required_argument, NULL, ARG_ROOT },
cc171228 499 { "image", required_argument, NULL, ARG_IMAGE },
c0dfcb31
LP
500 { "header", no_argument, NULL, ARG_HEADER },
501 { "identifier", required_argument, NULL, 't' },
502 { "priority", required_argument, NULL, 'p' },
196dedd5 503 { "facility", required_argument, NULL, ARG_FACILITY },
c0dfcb31
LP
504 { "grep", required_argument, NULL, 'g' },
505 { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
506 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
507 { "interval", required_argument, NULL, ARG_INTERVAL },
508 { "verify", no_argument, NULL, ARG_VERIFY },
509 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
510 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
511 { "cursor", required_argument, NULL, 'c' },
512 { "cursor-file", required_argument, NULL, ARG_CURSOR_FILE },
513 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
514 { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
515 { "since", required_argument, NULL, 'S' },
516 { "until", required_argument, NULL, 'U' },
517 { "unit", required_argument, NULL, 'u' },
518 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
519 { "field", required_argument, NULL, 'F' },
520 { "fields", no_argument, NULL, 'N' },
521 { "catalog", no_argument, NULL, 'x' },
522 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
523 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
524 { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
525 { "reverse", no_argument, NULL, 'r' },
526 { "machine", required_argument, NULL, 'M' },
527 { "utc", no_argument, NULL, ARG_UTC },
528 { "flush", no_argument, NULL, ARG_FLUSH },
529 { "relinquish-var", no_argument, NULL, ARG_RELINQUISH_VAR },
530 { "smart-relinquish-var", no_argument, NULL, ARG_SMART_RELINQUISH_VAR },
531 { "sync", no_argument, NULL, ARG_SYNC },
532 { "rotate", no_argument, NULL, ARG_ROTATE },
533 { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
534 { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
535 { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
536 { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
537 { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
6b25db87 538 { "namespace", required_argument, NULL, ARG_NAMESPACE },
eb9da376 539 {}
0d43c694
LP
540 };
541
2100675e 542 int c, r;
0d43c694
LP
543
544 assert(argc >= 0);
545 assert(argv);
546
2de6b06b 547 while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0)
0d43c694
LP
548
549 switch (c) {
550
551 case 'h':
37ec0fdd 552 return help();
0d43c694
LP
553
554 case ARG_VERSION:
3f6fd1ba 555 return version();
0d43c694
LP
556
557 case ARG_NO_PAGER:
0221d68a 558 arg_pager_flags |= PAGER_DISABLE;
0d43c694
LP
559 break;
560
1b12a7b5 561 case 'e':
0221d68a 562 arg_pager_flags |= PAGER_JUMP_TO_END;
fe59e38b 563
97e1cc8b 564 if (arg_lines == ARG_LINES_DEFAULT)
fe59e38b
LP
565 arg_lines = 1000;
566
2dd9285b 567 arg_boot = true;
2dd9285b 568
1b12a7b5
HH
569 break;
570
0d43c694
LP
571 case 'f':
572 arg_follow = true;
2dd9285b 573
574 arg_boot = true;
575 arg_boot_id = SD_ID128_NULL;
576 arg_boot_offset = 0;
577
0d43c694
LP
578 break;
579
580 case 'o':
5c828e66
LP
581 if (streq(optarg, "help")) {
582 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
583 return 0;
584 }
585
1705594f 586 arg_output = output_mode_from_string(optarg);
0491150b 587 if (arg_output < 0)
7211c853 588 return log_error_errno(arg_output, "Unknown output format '%s'.", optarg);
df50185b 589
8e044443 590 if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
edfb521a
ZJS
591 arg_quiet = true;
592
5a1355d8
FS
593 if (OUTPUT_MODE_IS_JSON(arg_output))
594 arg_json_format_flags = output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO;
faf20d4c
FS
595 else
596 arg_json_format_flags = JSON_FORMAT_OFF;
597
0d43c694
LP
598 break;
599
98a6e132 600 case 'l':
e3657ecd
ZJS
601 arg_full = true;
602 break;
603
2b8f6883
ZJS
604 case ARG_NO_FULL:
605 arg_full = false;
606 break;
607
0d43c694 608 case 'a':
cd4b13e0 609 arg_all = true;
0d43c694
LP
610 break;
611
2100675e 612 case 'n':
1705594f 613 if (optarg) {
48382487 614 if (streq(optarg, "all"))
97e1cc8b 615 arg_lines = ARG_LINES_ALL;
48382487
JJ
616 else {
617 r = safe_atoi(optarg, &arg_lines);
0491150b
LP
618 if (r < 0 || arg_lines < 0)
619 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse lines '%s'", optarg);
1705594f 620 }
96088db0 621 } else {
48382487 622 arg_lines = 10;
96088db0
LP
623
624 /* Hmm, no argument? Maybe the next
625 * word on the command line is
626 * supposed to be the argument? Let's
627 * see if there is one, and is
48382487
JJ
628 * parsable. */
629 if (optind < argc) {
630 int n;
631 if (streq(argv[optind], "all")) {
97e1cc8b 632 arg_lines = ARG_LINES_ALL;
48382487
JJ
633 optind++;
634 } else if (safe_atoi(argv[optind], &n) >= 0 && n >= 0) {
635 arg_lines = n;
636 optind++;
637 }
638 }
96088db0 639 }
1705594f 640
2100675e
LP
641 break;
642
e91af489
LP
643 case ARG_NO_TAIL:
644 arg_no_tail = true;
645 break;
646
39f7f5c1 647 case ARG_NEW_ID128:
7560fffc 648 arg_action = ACTION_NEW_ID128;
55ee336c
LP
649 break;
650
43673799
LP
651 case 'q':
652 arg_quiet = true;
490e567d 653 break;
43673799 654
9e8a535f
LP
655 case 'm':
656 arg_merge = true;
2bd3c38a
LP
657 break;
658
8453f062
ZJS
659 case ARG_THIS_BOOT:
660 arg_boot = true;
48904825 661 arg_boot_id = SD_ID128_NULL;
662 arg_boot_offset = 0;
8453f062
ZJS
663 break;
664
59cea26a 665 case 'b':
d121b396 666 arg_boot = true;
48904825 667 arg_boot_id = SD_ID128_NULL;
668 arg_boot_offset = 0;
6cebe83c 669
442e2def 670 if (optarg) {
909dea0c 671 r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
48904825 672 if (r < 0)
673 return log_error_errno(r, "Failed to parse boot descriptor '%s'", optarg);
674
675 arg_boot = r;
676
677 /* Hmm, no argument? Maybe the next
678 * word on the command line is
679 * supposed to be the argument? Let's
680 * see if there is one and is parsable
681 * as a boot descriptor... */
682 } else if (optind < argc) {
683 r = parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset);
684 if (r >= 0) {
685 arg_boot = r;
6cebe83c 686 optind++;
48904825 687 }
6cebe83c 688 }
59cea26a
LP
689 break;
690
f1188074
ZJS
691 case ARG_LIST_BOOTS:
692 arg_action = ACTION_LIST_BOOTS;
693 break;
694
99271804 695 case 'k':
d121b396 696 arg_boot = arg_dmesg = true;
99271804
ZJS
697 break;
698
3f3a438f
ZJS
699 case ARG_SYSTEM:
700 arg_journal_type |= SD_JOURNAL_SYSTEM;
701 break;
702
703 case ARG_USER:
704 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
705 break;
706
b6741478
LP
707 case 'M':
708 arg_machine = optarg;
709 break;
710
6b25db87
LP
711 case ARG_NAMESPACE:
712 if (streq(optarg, "*")) {
713 arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
714 arg_namespace = NULL;
715 } else if (startswith(optarg, "+")) {
716 arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
717 arg_namespace = optarg + 1;
718 } else if (isempty(optarg)) {
719 arg_namespace_flags = 0;
720 arg_namespace = NULL;
721 } else {
722 arg_namespace_flags = 0;
723 arg_namespace = optarg;
724 }
725
726 break;
727
a963990f
LP
728 case 'D':
729 arg_directory = optarg;
730 break;
731
8d98da3f 732 case ARG_FILE:
5d1ce257
LP
733 if (streq(optarg, "-"))
734 /* An undocumented feature: we can read journal files from STDIN. We don't document
735 * this though, since after all we only support this for mmap-able, seekable files, and
736 * not for example pipes which are probably the primary usecase for reading things from
737 * STDIN. To avoid confusion we hence don't document this feature. */
738 arg_file_stdin = true;
739 else {
544e146b 740 r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
5d1ce257
LP
741 if (r < 0)
742 return log_error_errno(r, "Failed to add paths: %m");
743 }
8d98da3f
ZJS
744 break;
745
13cbf3a5 746 case ARG_ROOT:
614b022c 747 r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
cc171228
LP
748 if (r < 0)
749 return r;
750 break;
751
752 case ARG_IMAGE:
614b022c 753 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
0f03c2a4
LP
754 if (r < 0)
755 return r;
13cbf3a5
ZJS
756 break;
757
8f14c832
LP
758 case 'c':
759 arg_cursor = optarg;
760 break;
761
d9e15cbd
JS
762 case ARG_CURSOR_FILE:
763 arg_cursor_file = optarg;
764 break;
765
248fc619
ZJS
766 case ARG_AFTER_CURSOR:
767 arg_after_cursor = optarg;
768 break;
769
770 case ARG_SHOW_CURSOR:
771 arg_show_cursor = true;
772 break;
773
dca6219e 774 case ARG_HEADER:
7560fffc
LP
775 arg_action = ACTION_PRINT_HEADER;
776 break;
777
feb12d3e
LP
778 case ARG_VERIFY:
779 arg_action = ACTION_VERIFY;
780 break;
781
a1a03e30
LP
782 case ARG_DISK_USAGE:
783 arg_action = ACTION_DISK_USAGE;
784 break;
785
dbd2a83f
LP
786 case ARG_VACUUM_SIZE:
787 r = parse_size(optarg, 1024, &arg_vacuum_size);
0491150b
LP
788 if (r < 0)
789 return log_error_errno(r, "Failed to parse vacuum size: %s", optarg);
dbd2a83f 790
8df64fd0 791 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
dbd2a83f
LP
792 break;
793
8580d1f7
LP
794 case ARG_VACUUM_FILES:
795 r = safe_atou64(optarg, &arg_vacuum_n_files);
0491150b
LP
796 if (r < 0)
797 return log_error_errno(r, "Failed to parse vacuum files: %s", optarg);
8580d1f7 798
8df64fd0 799 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
8580d1f7
LP
800 break;
801
dbd2a83f
LP
802 case ARG_VACUUM_TIME:
803 r = parse_sec(optarg, &arg_vacuum_time);
0491150b
LP
804 if (r < 0)
805 return log_error_errno(r, "Failed to parse vacuum time: %s", optarg);
dbd2a83f 806
8df64fd0 807 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
dbd2a83f
LP
808 break;
809
349cc4a5 810#if HAVE_GCRYPT
b8547c10
SL
811 case ARG_FORCE:
812 arg_force = true;
813 break;
814
7560fffc
LP
815 case ARG_SETUP_KEYS:
816 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
817 break;
818
baed47c3 819 case ARG_VERIFY_KEY:
e50412ef
ZJS
820 r = free_and_strdup(&arg_verify_key, optarg);
821 if (r < 0)
822 return r;
309c6b19 823 /* Use memset not explicit_bzero() or similar so this doesn't look confusing
1075122f
ZJS
824 * in ps or htop output. */
825 memset(optarg, 'x', strlen(optarg));
e50412ef 826
0491150b 827 arg_action = ACTION_VERIFY;
e50412ef 828 arg_merge = false;
4da416aa
LP
829 break;
830
baed47c3 831 case ARG_INTERVAL:
7f602784 832 r = parse_sec(optarg, &arg_interval);
0491150b
LP
833 if (r < 0 || arg_interval <= 0)
834 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
835 "Failed to parse sealing key change interval: %s", optarg);
14d10188 836 break;
feb12d3e
LP
837#else
838 case ARG_SETUP_KEYS:
839 case ARG_VERIFY_KEY:
840 case ARG_INTERVAL:
b8547c10 841 case ARG_FORCE:
0491150b
LP
842 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
843 "Compiled without forward-secure sealing support.");
feb12d3e 844#endif
14d10188 845
941e990d
LP
846 case 'p': {
847 const char *dots;
848
849 dots = strstr(optarg, "..");
850 if (dots) {
e7238caf 851 _cleanup_free_ char *a = NULL;
941e990d
LP
852 int from, to, i;
853
854 /* a range */
855 a = strndup(optarg, dots - optarg);
856 if (!a)
857 return log_oom();
858
859 from = log_level_from_string(a);
860 to = log_level_from_string(dots + 2);
941e990d 861
0491150b 862 if (from < 0 || to < 0)
7211c853 863 return log_error_errno(from < 0 ? from : to,
0491150b 864 "Failed to parse log level range %s", optarg);
941e990d
LP
865
866 arg_priorities = 0;
867
868 if (from < to) {
869 for (i = from; i <= to; i++)
870 arg_priorities |= 1 << i;
871 } else {
872 for (i = to; i <= from; i++)
873 arg_priorities |= 1 << i;
874 }
875
876 } else {
877 int p, i;
878
879 p = log_level_from_string(optarg);
0491150b 880 if (p < 0)
7211c853 881 return log_error_errno(p, "Unknown log level %s", optarg);
941e990d
LP
882
883 arg_priorities = 0;
884
885 for (i = 0; i <= p; i++)
886 arg_priorities |= 1 << i;
887 }
888
889 break;
890 }
891
196dedd5
ZJS
892 case ARG_FACILITY: {
893 const char *p;
894
895 for (p = optarg;;) {
896 _cleanup_free_ char *fac = NULL;
897 int num;
898
899 r = extract_first_word(&p, &fac, ",", 0);
900 if (r < 0)
901 return log_error_errno(r, "Failed to parse facilities: %s", optarg);
902 if (r == 0)
903 break;
904
905 if (streq(fac, "help")) {
906 help_facilities();
907 return 0;
908 }
909
910 num = log_facility_unshifted_from_string(fac);
911 if (num < 0)
7211c853 912 return log_error_errno(num, "Bad --facility= argument \"%s\".", fac);
196dedd5 913
de7fef4b 914 if (set_ensure_put(&arg_facilities, NULL, INT_TO_PTR(num)) < 0)
196dedd5
ZJS
915 return log_oom();
916 }
917
918 break;
919 }
920
6becf48c 921#if HAVE_PCRE2
61c5f8a1
ZJS
922 case 'g':
923 arg_pattern = optarg;
6becf48c 924 break;
6becf48c 925
61c5f8a1
ZJS
926 case ARG_CASE_SENSITIVE:
927 if (optarg) {
928 r = parse_boolean(optarg);
929 if (r < 0)
930 return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
931 arg_case_sensitive = r;
932 } else
933 arg_case_sensitive = true;
934
935 break;
6becf48c
ZJS
936#else
937 case 'g':
61c5f8a1 938 case ARG_CASE_SENSITIVE:
2275b747 939 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Compiled without pattern matching support");
6becf48c
ZJS
940#endif
941
66f52924 942 case 'S':
cfbc22ab 943 r = parse_timestamp(optarg, &arg_since);
0491150b
LP
944 if (r < 0)
945 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
946 "Failed to parse timestamp: %s", optarg);
cfbc22ab
LP
947 arg_since_set = true;
948 break;
949
66f52924 950 case 'U':
cfbc22ab 951 r = parse_timestamp(optarg, &arg_until);
0491150b
LP
952 if (r < 0)
953 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
954 "Failed to parse timestamp: %s", optarg);
cfbc22ab
LP
955 arg_until_set = true;
956 break;
957
73083640
HH
958 case 't':
959 r = strv_extend(&arg_syslog_identifier, optarg);
960 if (r < 0)
961 return log_oom();
962 break;
963
7199aa96 964 case 'u':
b9e40524
HH
965 r = strv_extend(&arg_system_units, optarg);
966 if (r < 0)
967 return log_oom();
ffa7cd15
DW
968 break;
969
7199aa96 970 case ARG_USER_UNIT:
b9e40524
HH
971 r = strv_extend(&arg_user_units, optarg);
972 if (r < 0)
973 return log_oom();
c3f60ec5
LP
974 break;
975
15119c16 976 case 'F':
69e714f3 977 arg_action = ACTION_LIST_FIELDS;
15119c16
LP
978 arg_field = optarg;
979 break;
980
69e714f3
LP
981 case 'N':
982 arg_action = ACTION_LIST_FIELD_NAMES;
983 break;
984
991e274b
LP
985 case ARG_NO_HOSTNAME:
986 arg_no_hostname = true;
987 break;
988
d4205751
LP
989 case 'x':
990 arg_catalog = true;
991 break;
992
993 case ARG_LIST_CATALOG:
994 arg_action = ACTION_LIST_CATALOG;
995 break;
996
54b7254c
ZJS
997 case ARG_DUMP_CATALOG:
998 arg_action = ACTION_DUMP_CATALOG;
999 break;
1000
d4205751
LP
1001 case ARG_UPDATE_CATALOG:
1002 arg_action = ACTION_UPDATE_CATALOG;
1003 break;
1004
d89d6c86
LN
1005 case 'r':
1006 arg_reverse = true;
1007 break;
1008
9fd29044
JS
1009 case ARG_UTC:
1010 arg_utc = true;
1011 break;
1012
74055aa7
LP
1013 case ARG_FLUSH:
1014 arg_action = ACTION_FLUSH;
1015 break;
1016
c0dfcb31
LP
1017 case ARG_SMART_RELINQUISH_VAR: {
1018 int root_mnt_id, log_mnt_id;
1019
1020 /* Try to be smart about relinquishing access to /var/log/journal/ during shutdown:
1021 * if it's on the same mount as the root file system there's no point in
1022 * relinquishing access and we can leave journald write to it until the very last
1023 * moment. */
1024
1025 r = path_get_mnt_id("/", &root_mnt_id);
1026 if (r < 0)
1027 log_debug_errno(r, "Failed to get root mount ID, ignoring: %m");
1028 else {
1029 r = path_get_mnt_id("/var/log/journal/", &log_mnt_id);
1030 if (r < 0)
1031 log_debug_errno(r, "Failed to get journal directory mount ID, ignoring: %m");
1032 else if (root_mnt_id == log_mnt_id) {
1033 log_debug("/var/log/journal/ is on root file system, not relinquishing access to /var.");
1034 return 0;
1035 } else
1036 log_debug("/var/log/journal/ is not on the root file system, relinquishing access to it.");
1037 }
1038
1039 _fallthrough_;
1040 }
1041
1042 case ARG_RELINQUISH_VAR:
1043 arg_action = ACTION_RELINQUISH_VAR;
1044 break;
1045
e3fdfb49 1046 case ARG_ROTATE:
8df64fd0 1047 arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
e3fdfb49
EV
1048 break;
1049
94b65516
LP
1050 case ARG_SYNC:
1051 arg_action = ACTION_SYNC;
1052 break;
1053
cc25a67e
LK
1054 case ARG_OUTPUT_FIELDS: {
1055 _cleanup_strv_free_ char **v = NULL;
1056
1057 v = strv_split(optarg, ",");
1058 if (!v)
1059 return log_oom();
1060
1cc6c93a
YW
1061 if (!arg_output_fields)
1062 arg_output_fields = TAKE_PTR(v);
1063 else {
cc25a67e
LK
1064 r = strv_extend_strv(&arg_output_fields, v, true);
1065 if (r < 0)
1066 return log_oom();
1067 }
1068 break;
1069 }
1070
eb9da376 1071 case '?':
0d43c694 1072 return -EINVAL;
eb9da376
LP
1073
1074 default:
04499a70 1075 assert_not_reached();
0d43c694 1076 }
0d43c694 1077
70af7b8a 1078 if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT)
e91af489
LP
1079 arg_lines = 10;
1080
d7a0f1f4
FS
1081 if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root + !!arg_image > 1)
1082 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1083 "Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root=, --image=.");
8d98da3f 1084
d7a0f1f4
FS
1085 if (arg_since_set && arg_until_set && arg_since > arg_until)
1086 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1087 "--since= must be before --until=.");
cfbc22ab 1088
d7a0f1f4
FS
1089 if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
1090 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1091 "Please specify only one of --since=, --cursor=, and --after-cursor.");
cfbc22ab 1092
d7a0f1f4
FS
1093 if (arg_follow && arg_reverse)
1094 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1095 "Please specify either --reverse= or --follow=, not both.");
d89d6c86 1096
d7a0f1f4
FS
1097 if (!IN_SET(arg_action, ACTION_SHOW, ACTION_DUMP_CATALOG, ACTION_LIST_CATALOG) && optind < argc)
1098 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1099 "Extraneous arguments starting with '%s'",
1100 argv[optind]);
0b6b7c20 1101
d7a0f1f4
FS
1102 if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && arg_merge)
1103 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1104 "Using --boot or --list-boots with --merge is not supported.");
596a2329 1105
e79d0b59 1106 if (!strv_isempty(arg_system_units) && arg_journal_type == SD_JOURNAL_CURRENT_USER) {
52051dd8
LP
1107 /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user
1108 * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice
1109 * to users, and automatically turn --unit= into --user-unit= if combined with --user. */
1110 r = strv_extend_strv(&arg_user_units, arg_system_units, true);
1111 if (r < 0)
e50412ef 1112 return r;
52051dd8
LP
1113
1114 arg_system_units = strv_free(arg_system_units);
1115 }
1116
61c5f8a1
ZJS
1117#if HAVE_PCRE2
1118 if (arg_pattern) {
1119 unsigned flags;
1120
9200bb30
LP
1121 r = dlopen_pcre2();
1122 if (r < 0)
1123 return r;
1124
61c5f8a1
ZJS
1125 if (arg_case_sensitive >= 0)
1126 flags = !arg_case_sensitive * PCRE2_CASELESS;
1127 else {
9200bb30 1128 _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
61c5f8a1 1129 bool has_case;
9200bb30 1130 _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
61c5f8a1 1131
9200bb30 1132 md = sym_pcre2_match_data_create(1, NULL);
61c5f8a1
ZJS
1133 if (!md)
1134 return log_oom();
1135
1136 r = pattern_compile("[[:upper:]]", 0, &cs);
1137 if (r < 0)
1138 return r;
1139
9200bb30 1140 r = sym_pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
61c5f8a1
ZJS
1141 has_case = r >= 0;
1142
1143 flags = !has_case * PCRE2_CASELESS;
1144 }
1145
1146 log_debug("Doing case %s matching based on %s",
1147 flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
1148 arg_case_sensitive >= 0 ? "request" : "pattern casing");
1149
1150 r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern);
1151 if (r < 0)
1152 return r;
1153 }
1154#endif
1155
0d43c694
LP
1156 return 1;
1157}
1158
a963990f 1159static int add_matches(sd_journal *j, char **args) {
4e602943 1160 bool have_term = false;
59cea26a 1161
a963990f 1162 assert(j);
59cea26a 1163
a963990f 1164 STRV_FOREACH(i, args) {
52aeb63c 1165 int r;
59cea26a 1166
4e602943
ZJS
1167 if (streq(*i, "+")) {
1168 if (!have_term)
1169 break;
cbdca852 1170 r = sd_journal_add_disjunction(j);
4e602943
ZJS
1171 have_term = false;
1172
1173 } else if (path_is_absolute(*i)) {
e1873695 1174 _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
a963990f 1175 struct stat st;
e5124088 1176
a5648b80 1177 r = chase_symlinks(*i, NULL, CHASE_TRAIL_SLASH, &p, NULL);
e1873695
LP
1178 if (r < 0)
1179 return log_error_errno(r, "Couldn't canonicalize path: %m");
e5124088 1180
e1873695 1181 if (lstat(p, &st) < 0)
4a62c710 1182 return log_error_errno(errno, "Couldn't stat file: %m");
e5124088 1183
68fee104 1184 if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
e1873695 1185 if (executable_is_script(p, &interpreter) > 0) {
c2b2df60 1186 _cleanup_free_ char *comm = NULL;
68fee104 1187
e1873695 1188 comm = strndup(basename(p), 15);
68fee104
ZJS
1189 if (!comm)
1190 return log_oom();
1191
b910cc72 1192 t = strjoin("_COMM=", comm);
795ab08f
MS
1193 if (!t)
1194 return log_oom();
68fee104
ZJS
1195
1196 /* Append _EXE only if the interpreter is not a link.
73e231ab 1197 Otherwise, it might be outdated often. */
795ab08f 1198 if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
b910cc72 1199 t2 = strjoin("_EXE=", interpreter);
68fee104
ZJS
1200 if (!t2)
1201 return log_oom();
1202 }
795ab08f 1203 } else {
b910cc72 1204 t = strjoin("_EXE=", p);
795ab08f
MS
1205 if (!t)
1206 return log_oom();
1207 }
1208
1209 r = sd_journal_add_match(j, t, 0);
1210
1211 if (r >=0 && t2)
1212 r = sd_journal_add_match(j, t2, 0);
1213
1214 } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
e1873695 1215 r = add_matches_for_device(j, p);
795ab08f
MS
1216 if (r < 0)
1217 return r;
baaa35ad
ZJS
1218 } else
1219 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1220 "File is neither a device node, nor regular file, nor executable: %s",
1221 *i);
e5124088 1222
4e602943 1223 have_term = true;
4e602943 1224 } else {
cbdca852 1225 r = sd_journal_add_match(j, *i, 0);
4e602943
ZJS
1226 have_term = true;
1227 }
e5124088 1228
23bbb0de
MS
1229 if (r < 0)
1230 return log_error_errno(r, "Failed to add match '%s': %m", *i);
de7b95cd
LP
1231 }
1232
baaa35ad
ZJS
1233 if (!strv_isempty(args) && !have_term)
1234 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1235 "\"+\" can only be used between terms");
4e602943 1236
a963990f
LP
1237 return 0;
1238}
1239
9530e0d0
LP
1240static void boot_id_free_all(BootId *l) {
1241
1242 while (l) {
1243 BootId *i = l;
1244 LIST_REMOVE(boot_list, l, i);
1245 free(i);
1246 }
1247}
1248
dc009662
LP
1249static int discover_next_boot(sd_journal *j,
1250 sd_id128_t previous_boot_id,
1251 bool advance_older,
1252 BootId **ret) {
45bc27b6 1253
45bc27b6 1254 _cleanup_free_ BootId *next_boot = NULL;
492f148f 1255 char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
dc009662
LP
1256 sd_id128_t boot_id;
1257 int r;
ea7061e4
JJ
1258
1259 assert(j);
dc009662 1260 assert(ret);
596a2329
JJ
1261
1262 /* We expect the journal to be on the last position of a boot
1263 * (in relation to the direction we are going), so that the next
1264 * invocation of sd_journal_next/previous will be from a different
1265 * boot. We then collect any information we desire and then jump
1266 * to the last location of the new boot by using a _BOOT_ID match
1267 * coming from the other journal direction. */
1268
1269 /* Make sure we aren't restricted by any _BOOT_ID matches, so that
1270 * we can actually advance to a *different* boot. */
1271 sd_journal_flush_matches(j);
1272
dc009662
LP
1273 do {
1274 if (advance_older)
1275 r = sd_journal_previous(j);
1276 else
1277 r = sd_journal_next(j);
1278 if (r < 0)
1279 return r;
1280 else if (r == 0)
1281 return 0; /* End of journal, yay. */
1282
1283 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1284 if (r < 0)
1285 return r;
1286
1287 /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
1288 * normally, this will only require a single iteration, as we seeked to the last entry of the previous
1289 * boot entry already. However, it might happen that the per-journal-field entry arrays are less
1290 * complete than the main entry array, and hence might reference an entry that's not actually the last
1291 * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
1292 * speed things up, but let's not trust that it is complete, and hence, manually advance as
1293 * necessary. */
1294
1295 } while (sd_id128_equal(boot_id, previous_boot_id));
596a2329 1296
45bc27b6 1297 next_boot = new0(BootId, 1);
596a2329 1298 if (!next_boot)
b56d608e 1299 return -ENOMEM;
f1188074 1300
dc009662 1301 next_boot->id = boot_id;
f1188074 1302
d1bf9dc9
LP
1303 r = sd_journal_get_realtime_usec(j, &next_boot->first);
1304 if (r < 0)
1305 return r;
ea7061e4 1306
596a2329 1307 /* Now seek to the last occurrence of this boot ID. */
0d5765f7 1308 sd_id128_to_string(next_boot->id, match + STRLEN("_BOOT_ID="));
596a2329
JJ
1309 r = sd_journal_add_match(j, match, sizeof(match) - 1);
1310 if (r < 0)
1311 return r;
f1188074 1312
596a2329
JJ
1313 if (advance_older)
1314 r = sd_journal_seek_head(j);
1315 else
1316 r = sd_journal_seek_tail(j);
1317 if (r < 0)
1318 return r;
f1188074 1319
596a2329
JJ
1320 if (advance_older)
1321 r = sd_journal_next(j);
1322 else
1323 r = sd_journal_previous(j);
1324 if (r < 0)
1325 return r;
baaa35ad
ZJS
1326 else if (r == 0)
1327 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
1328 "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */
f1188074 1329
d1bf9dc9
LP
1330 r = sd_journal_get_realtime_usec(j, &next_boot->last);
1331 if (r < 0)
1332 return r;
596a2329 1333
1cc6c93a 1334 *ret = TAKE_PTR(next_boot);
9530e0d0 1335
596a2329
JJ
1336 return 0;
1337}
1338
45bc27b6
LP
1339static int get_boots(
1340 sd_journal *j,
1341 BootId **boots,
07ff6b08
ZJS
1342 sd_id128_t *boot_id,
1343 int offset) {
45bc27b6 1344
596a2329
JJ
1345 bool skip_once;
1346 int r, count = 0;
03677889 1347 BootId *head = NULL, *tail = NULL;
07ff6b08 1348 const bool advance_older = boot_id && offset <= 0;
dc009662 1349 sd_id128_t previous_boot_id;
596a2329
JJ
1350
1351 assert(j);
f1188074 1352
596a2329
JJ
1353 /* Adjust for the asymmetry that offset 0 is
1354 * the last (and current) boot, while 1 is considered the
1355 * (chronological) first boot in the journal. */
592855c3 1356 skip_once = boot_id && sd_id128_is_null(*boot_id) && offset <= 0;
596a2329
JJ
1357
1358 /* Advance to the earliest/latest occurrence of our reference
1359 * boot ID (taking our lookup direction into account), so that
1360 * discover_next_boot() can do its job.
1361 * If no reference is given, the journal head/tail will do,
1362 * they're "virtual" boots after all. */
07ff6b08 1363 if (boot_id && !sd_id128_is_null(*boot_id)) {
492f148f 1364 char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
596a2329
JJ
1365
1366 sd_journal_flush_matches(j);
1367
0d5765f7 1368 sd_id128_to_string(*boot_id, match + STRLEN("_BOOT_ID="));
596a2329 1369 r = sd_journal_add_match(j, match, sizeof(match) - 1);
f1188074
ZJS
1370 if (r < 0)
1371 return r;
1372
596a2329 1373 if (advance_older)
c4fbc6b6 1374 r = sd_journal_seek_head(j); /* seek to oldest */
596a2329 1375 else
c4fbc6b6 1376 r = sd_journal_seek_tail(j); /* seek to newest */
f1188074
ZJS
1377 if (r < 0)
1378 return r;
1379
596a2329 1380 if (advance_older)
c4fbc6b6 1381 r = sd_journal_next(j); /* read the oldest entry */
596a2329 1382 else
c4fbc6b6 1383 r = sd_journal_previous(j); /* read the most recently added entry */
f1188074
ZJS
1384 if (r < 0)
1385 return r;
1386 else if (r == 0)
596a2329 1387 goto finish;
07ff6b08 1388 else if (offset == 0) {
596a2329
JJ
1389 count = 1;
1390 goto finish;
1391 }
c4fbc6b6 1392
5238e957 1393 /* At this point the read pointer is positioned at the oldest/newest occurrence of the reference boot
c4fbc6b6
LP
1394 * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at
1395 * the following entry, which must then have an older/newer boot ID */
596a2329 1396 } else {
c4fbc6b6 1397
596a2329 1398 if (advance_older)
c4fbc6b6 1399 r = sd_journal_seek_tail(j); /* seek to newest */
596a2329 1400 else
c4fbc6b6 1401 r = sd_journal_seek_head(j); /* seek to oldest */
f1188074
ZJS
1402 if (r < 0)
1403 return r;
1404
c4fbc6b6
LP
1405 /* No sd_journal_next()/_previous() here.
1406 *
1407 * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
1408 * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
1409 * entry we have. */
596a2329 1410 }
f1188074 1411
dc009662 1412 previous_boot_id = SD_ID128_NULL;
45bc27b6
LP
1413 for (;;) {
1414 _cleanup_free_ BootId *current = NULL;
f1188074 1415
dc009662 1416 r = discover_next_boot(j, previous_boot_id, advance_older, &current);
596a2329 1417 if (r < 0) {
9530e0d0 1418 boot_id_free_all(head);
596a2329 1419 return r;
ea7061e4 1420 }
f1188074 1421
596a2329
JJ
1422 if (!current)
1423 break;
1424
dc009662
LP
1425 previous_boot_id = current->id;
1426
07ff6b08 1427 if (boot_id) {
596a2329 1428 if (!skip_once)
07ff6b08 1429 offset += advance_older ? 1 : -1;
596a2329
JJ
1430 skip_once = false;
1431
07ff6b08 1432 if (offset == 0) {
596a2329 1433 count = 1;
07ff6b08 1434 *boot_id = current->id;
596a2329
JJ
1435 break;
1436 }
1437 } else {
ec02a6c9
HK
1438 LIST_FOREACH(boot_list, id, head) {
1439 if (sd_id128_equal(id->id, current->id)) {
1440 /* boot id already stored, something wrong with the journal files */
1441 /* exiting as otherwise this problem would cause forever loop */
1442 goto finish;
1443 }
1444 }
596a2329 1445 LIST_INSERT_AFTER(boot_list, head, tail, current);
1cc6c93a 1446 tail = TAKE_PTR(current);
596a2329
JJ
1447 count++;
1448 }
f1188074
ZJS
1449 }
1450
596a2329
JJ
1451finish:
1452 if (boots)
1453 *boots = head;
1454
1455 sd_journal_flush_matches(j);
1456
1457 return count;
ea7061e4
JJ
1458}
1459
1460static int list_boots(sd_journal *j) {
5a1355d8 1461 _cleanup_(table_unrefp) Table *table = NULL;
03677889 1462 BootId *all_ids;
5a1355d8 1463 int count, i, r;
ea7061e4
JJ
1464
1465 assert(j);
1466
596a2329 1467 count = get_boots(j, &all_ids, NULL, 0);
b56d608e
LP
1468 if (count < 0)
1469 return log_error_errno(count, "Failed to determine boots: %m");
1470 if (count == 0)
596a2329 1471 return count;
ea7061e4 1472
8e4b9a25 1473 table = table_new("idx", "boot id", "first entry", "last entry");
5a1355d8
FS
1474 if (!table)
1475 return log_oom();
f1188074 1476
5a1355d8
FS
1477 if (arg_full)
1478 table_set_width(table, 0);
f1188074 1479
8e4b9a25
FS
1480 r = table_set_json_field_name(table, 0, "index");
1481 if (r < 0)
1482 return log_error_errno(r, "Failed to set JSON field name of column 0: %m");
1483
f01aafd2
FS
1484 (void) table_set_sort(table, (size_t) 0);
1485 (void) table_set_reverse(table, 0, arg_reverse);
1486
596a2329 1487 i = 0;
9530e0d0 1488 LIST_FOREACH(boot_list, id, all_ids) {
5a1355d8
FS
1489 r = table_add_many(table,
1490 TABLE_INT, i - count + 1,
1491 TABLE_SET_ALIGN_PERCENT, 100,
1492 TABLE_ID128, id->id,
1493 TABLE_TIMESTAMP, id->first,
1494 TABLE_TIMESTAMP, id->last);
1495 if (r < 0)
1496 return table_log_add_error(r);
596a2329 1497 i++;
d121b396 1498 }
a963990f 1499
5a1355d8
FS
1500 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet);
1501 if (r < 0)
1502 return table_log_print_error(r);
1503
9530e0d0
LP
1504 boot_id_free_all(all_ids);
1505
a331b5e6
JJ
1506 return 0;
1507}
1508
1509static int add_boot(sd_journal *j) {
492f148f 1510 char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
07ff6b08 1511 sd_id128_t boot_id;
442e2def 1512 int r;
a331b5e6
JJ
1513
1514 assert(j);
1515
d121b396 1516 if (!arg_boot)
a331b5e6
JJ
1517 return 0;
1518
592855c3
ZJS
1519 /* Take a shortcut and use the current boot_id, which we can do very quickly.
1520 * We can do this only when we logs are coming from the current machine,
1521 * so take the slow path if log location is specified. */
3bbaff3e 1522 if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
0a175093 1523 !arg_directory && !arg_file && !arg_root)
b6741478 1524 return add_match_this_boot(j, arg_machine);
a331b5e6 1525
07ff6b08
ZJS
1526 boot_id = arg_boot_id;
1527 r = get_boots(j, NULL, &boot_id, arg_boot_offset);
596a2329
JJ
1528 assert(r <= 1);
1529 if (r <= 0) {
4bbccb02 1530 const char *reason = (r == 0) ? "No such boot ID in journal" : strerror_safe(r);
596a2329
JJ
1531
1532 if (sd_id128_is_null(arg_boot_id))
c34e9399
JS
1533 log_error("Data from the specified boot (%+i) is not available: %s",
1534 arg_boot_offset, reason);
d121b396 1535 else
c34e9399
JS
1536 log_error("Data from the specified boot ("SD_ID128_FORMAT_STR") is not available: %s",
1537 SD_ID128_FORMAT_VAL(arg_boot_id), reason);
596a2329
JJ
1538
1539 return r == 0 ? -ENODATA : r;
a331b5e6
JJ
1540 }
1541
0d5765f7 1542 sd_id128_to_string(boot_id, match + STRLEN("_BOOT_ID="));
d121b396
ZJS
1543
1544 r = sd_journal_add_match(j, match, sizeof(match) - 1);
23bbb0de
MS
1545 if (r < 0)
1546 return log_error_errno(r, "Failed to add match: %m");
a331b5e6
JJ
1547
1548 r = sd_journal_add_conjunction(j);
1549 if (r < 0)
b56d608e 1550 return log_error_errno(r, "Failed to add conjunction: %m");
a331b5e6
JJ
1551
1552 return 0;
a963990f
LP
1553}
1554
99271804
ZJS
1555static int add_dmesg(sd_journal *j) {
1556 int r;
1557 assert(j);
1558
1559 if (!arg_dmesg)
1560 return 0;
1561
fbd0b64f
LP
1562 r = sd_journal_add_match(j, "_TRANSPORT=kernel",
1563 STRLEN("_TRANSPORT=kernel"));
23bbb0de
MS
1564 if (r < 0)
1565 return log_error_errno(r, "Failed to add match: %m");
99271804
ZJS
1566
1567 r = sd_journal_add_conjunction(j);
1568 if (r < 0)
b56d608e 1569 return log_error_errno(r, "Failed to add conjunction: %m");
99271804
ZJS
1570
1571 return 0;
1572}
1573
b56d608e
LP
1574static int get_possible_units(
1575 sd_journal *j,
1576 const char *fields,
1577 char **patterns,
1578 Set **units) {
1579
c2b2df60 1580 _cleanup_set_free_free_ Set *found = NULL;
ea18a4b5 1581 const char *field;
c3f60ec5 1582 int r;
ea18a4b5 1583
d5099efc 1584 found = set_new(&string_hash_ops);
ea18a4b5 1585 if (!found)
b56d608e 1586 return -ENOMEM;
ea18a4b5
ZJS
1587
1588 NULSTR_FOREACH(field, fields) {
1589 const void *data;
1590 size_t size;
1591
1592 r = sd_journal_query_unique(j, field);
1593 if (r < 0)
1594 return r;
1595
1596 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
de010b0b 1597 char *eq;
ea18a4b5
ZJS
1598 size_t prefix;
1599 _cleanup_free_ char *u = NULL;
1600
1601 eq = memchr(data, '=', size);
1602 if (eq)
1603 prefix = eq - (char*) data + 1;
1604 else
1605 prefix = 0;
1606
1607 u = strndup((char*) data + prefix, size - prefix);
1608 if (!u)
b56d608e 1609 return -ENOMEM;
ea18a4b5
ZJS
1610
1611 STRV_FOREACH(pattern, patterns)
1612 if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) {
1613 log_debug("Matched %s with pattern %s=%s", u, field, *pattern);
1614
1615 r = set_consume(found, u);
1616 u = NULL;
1617 if (r < 0 && r != -EEXIST)
1618 return r;
1619
1620 break;
1621 }
1622 }
1623 }
1624
1cc6c93a
YW
1625 *units = TAKE_PTR(found);
1626
ea18a4b5
ZJS
1627 return 0;
1628}
1629
1630/* This list is supposed to return the superset of unit names
1631 * possibly matched by rules added with add_matches_for_unit... */
1632#define SYSTEM_UNITS \
1633 "_SYSTEMD_UNIT\0" \
1634 "COREDUMP_UNIT\0" \
1635 "UNIT\0" \
1636 "OBJECT_SYSTEMD_UNIT\0" \
1637 "_SYSTEMD_SLICE\0"
1638
1639/* ... and add_matches_for_user_unit */
1640#define USER_UNITS \
1641 "_SYSTEMD_USER_UNIT\0" \
1642 "USER_UNIT\0" \
1643 "COREDUMP_USER_UNIT\0" \
0e4a4f56
AP
1644 "OBJECT_SYSTEMD_USER_UNIT\0" \
1645 "_SYSTEMD_USER_SLICE\0"
ea18a4b5
ZJS
1646
1647static int add_units(sd_journal *j) {
1648 _cleanup_strv_free_ char **patterns = NULL;
1649 int r, count = 0;
c3f60ec5
LP
1650
1651 assert(j);
1652
b9e40524 1653 STRV_FOREACH(i, arg_system_units) {
ea18a4b5
ZJS
1654 _cleanup_free_ char *u = NULL;
1655
37cbc1d5 1656 r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &u);
7410616c
LP
1657 if (r < 0)
1658 return r;
ea18a4b5
ZJS
1659
1660 if (string_is_glob(u)) {
1661 r = strv_push(&patterns, u);
1662 if (r < 0)
1663 return r;
1664 u = NULL;
1665 } else {
1666 r = add_matches_for_unit(j, u);
1667 if (r < 0)
1668 return r;
1669 r = sd_journal_add_disjunction(j);
1670 if (r < 0)
1671 return r;
313cefa1 1672 count++;
ea18a4b5
ZJS
1673 }
1674 }
1675
1676 if (!strv_isempty(patterns)) {
1677 _cleanup_set_free_free_ Set *units = NULL;
ea18a4b5
ZJS
1678 char *u;
1679
1680 r = get_possible_units(j, SYSTEM_UNITS, patterns, &units);
b9e40524
HH
1681 if (r < 0)
1682 return r;
ea18a4b5 1683
90e74a66 1684 SET_FOREACH(u, units) {
ea18a4b5
ZJS
1685 r = add_matches_for_unit(j, u);
1686 if (r < 0)
1687 return r;
1688 r = sd_journal_add_disjunction(j);
1689 if (r < 0)
1690 return r;
313cefa1 1691 count++;
ea18a4b5 1692 }
b9e40524 1693 }
c3f60ec5 1694
97b11eed 1695 patterns = strv_free(patterns);
ea18a4b5 1696
b9e40524 1697 STRV_FOREACH(i, arg_user_units) {
ea18a4b5
ZJS
1698 _cleanup_free_ char *u = NULL;
1699
37cbc1d5 1700 r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &u);
7410616c
LP
1701 if (r < 0)
1702 return r;
c3f60ec5 1703
ea18a4b5
ZJS
1704 if (string_is_glob(u)) {
1705 r = strv_push(&patterns, u);
1706 if (r < 0)
1707 return r;
1708 u = NULL;
1709 } else {
1710 r = add_matches_for_user_unit(j, u, getuid());
1711 if (r < 0)
1712 return r;
1713 r = sd_journal_add_disjunction(j);
1714 if (r < 0)
1715 return r;
313cefa1 1716 count++;
ea18a4b5
ZJS
1717 }
1718 }
1719
1720 if (!strv_isempty(patterns)) {
1721 _cleanup_set_free_free_ Set *units = NULL;
ea18a4b5 1722 char *u;
b9e40524 1723
ea18a4b5 1724 r = get_possible_units(j, USER_UNITS, patterns, &units);
b9e40524
HH
1725 if (r < 0)
1726 return r;
1727
90e74a66 1728 SET_FOREACH(u, units) {
ea18a4b5
ZJS
1729 r = add_matches_for_user_unit(j, u, getuid());
1730 if (r < 0)
1731 return r;
1732 r = sd_journal_add_disjunction(j);
1733 if (r < 0)
1734 return r;
313cefa1 1735 count++;
ea18a4b5 1736 }
b9e40524 1737 }
c3f60ec5 1738
ea18a4b5
ZJS
1739 /* Complain if the user request matches but nothing whatsoever was
1740 * found, since otherwise everything would be matched. */
1741 if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0)
1742 return -ENODATA;
1743
cd34b3c6
HH
1744 r = sd_journal_add_conjunction(j);
1745 if (r < 0)
1746 return r;
1747
c3f60ec5
LP
1748 return 0;
1749}
1750
941e990d
LP
1751static int add_priorities(sd_journal *j) {
1752 char match[] = "PRIORITY=0";
1753 int i, r;
941e990d
LP
1754 assert(j);
1755
1756 if (arg_priorities == 0xFF)
1757 return 0;
1758
1759 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
1760 if (arg_priorities & (1 << i)) {
1761 match[sizeof(match)-2] = '0' + i;
1762
941e990d 1763 r = sd_journal_add_match(j, match, strlen(match));
23bbb0de
MS
1764 if (r < 0)
1765 return log_error_errno(r, "Failed to add match: %m");
941e990d
LP
1766 }
1767
cd34b3c6
HH
1768 r = sd_journal_add_conjunction(j);
1769 if (r < 0)
b56d608e 1770 return log_error_errno(r, "Failed to add conjunction: %m");
cd34b3c6 1771
941e990d
LP
1772 return 0;
1773}
1774
196dedd5
ZJS
1775static int add_facilities(sd_journal *j) {
1776 void *p;
196dedd5
ZJS
1777 int r;
1778
90e74a66 1779 SET_FOREACH(p, arg_facilities) {
196dedd5
ZJS
1780 char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
1781
1782 xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
1783
1784 r = sd_journal_add_match(j, match, strlen(match));
1785 if (r < 0)
1786 return log_error_errno(r, "Failed to add match: %m");
1787 }
1788
1789 return 0;
1790}
1791
73083640
HH
1792static int add_syslog_identifier(sd_journal *j) {
1793 int r;
73083640
HH
1794
1795 assert(j);
1796
1797 STRV_FOREACH(i, arg_syslog_identifier) {
6d946490 1798 _cleanup_free_ char *u = NULL;
73083640 1799
6d946490
YW
1800 u = strjoin("SYSLOG_IDENTIFIER=", *i);
1801 if (!u)
1802 return -ENOMEM;
73083640
HH
1803 r = sd_journal_add_match(j, u, 0);
1804 if (r < 0)
1805 return r;
1806 r = sd_journal_add_disjunction(j);
1807 if (r < 0)
1808 return r;
1809 }
1810
1811 r = sd_journal_add_conjunction(j);
1812 if (r < 0)
1813 return r;
1814
1815 return 0;
1816}
1817
7b6c92e6 1818#if HAVE_GCRYPT
f1b82359
ZJS
1819static int format_journal_url(
1820 const void *seed,
1821 size_t seed_size,
1822 uint64_t start,
1823 uint64_t interval,
1824 const char *hn,
1825 sd_id128_t machine,
1826 bool full,
1827 char **ret_url) {
1828 _cleanup_free_ char *url = NULL;
1829 _cleanup_fclose_ FILE *f = NULL;
1830 size_t url_size = 0;
1831 int r;
1832
1833 assert(seed);
1834 assert(seed_size > 0);
1835
1836 f = open_memstream_unlocked(&url, &url_size);
1837 if (!f)
1838 return -ENOMEM;
1839
1840 if (full)
1841 fputs("fss://", f);
1842
1843 for (size_t i = 0; i < seed_size; i++) {
1844 if (i > 0 && i % 3 == 0)
1845 fputc('-', f);
1846 fprintf(f, "%02x", ((uint8_t*) seed)[i]);
1847 }
1848
1849 fprintf(f, "/%"PRIx64"-%"PRIx64, start, interval);
1850
1851 if (full) {
1852 fprintf(f, "?machine=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(machine));
1853 if (hn)
1854 fprintf(f, ";hostname=%s", hn);
1855 }
1856
1857 r = fflush_and_check(f);
1858 if (r < 0)
1859 return r;
1860
1861 f = safe_fclose(f);
1862 *ret_url = TAKE_PTR(url);
1863 return 0;
1864}
7b6c92e6 1865#endif
f1b82359 1866
7560fffc 1867static int setup_keys(void) {
349cc4a5 1868#if HAVE_GCRYPT
da012db0 1869 size_t mpk_size, seed_size, state_size;
9bff1410
LP
1870 _cleanup_(unlink_and_freep) char *k = NULL;
1871 _cleanup_free_ char *p = NULL;
7560fffc 1872 uint8_t *mpk, *seed, *state;
9bff1410 1873 _cleanup_close_ int fd = -1;
7560fffc 1874 sd_id128_t machine, boot;
b98e3866 1875 struct stat st;
9bff1410
LP
1876 uint64_t n;
1877 int r;
b98e3866
SL
1878
1879 r = stat("/var/log/journal", &st);
4c701096 1880 if (r < 0 && !IN_SET(errno, ENOENT, ENOTDIR))
4a62c710 1881 return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal");
b98e3866
SL
1882
1883 if (r < 0 || !S_ISDIR(st.st_mode)) {
1884 log_error("%s is not a directory, must be using persistent logging for FSS.",
1885 "/var/log/journal");
1886 return r < 0 ? -errno : -ENOTDIR;
1887 }
7560fffc
LP
1888
1889 r = sd_id128_get_machine(&machine);
23bbb0de
MS
1890 if (r < 0)
1891 return log_error_errno(r, "Failed to get machine ID: %m");
7560fffc
LP
1892
1893 r = sd_id128_get_boot(&boot);
23bbb0de
MS
1894 if (r < 0)
1895 return log_error_errno(r, "Failed to get boot ID: %m");
7560fffc 1896
baed47c3 1897 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
1898 SD_ID128_FORMAT_VAL(machine)) < 0)
1899 return log_oom();
1900
faf9da01
ZJS
1901 if (arg_force) {
1902 r = unlink(p);
9bff1410
LP
1903 if (r < 0 && errno != ENOENT)
1904 return log_error_errno(errno, "unlink(\"%s\") failed: %m", p);
1905 } else if (access(p, F_OK) >= 0)
1906 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
1907 "Sealing key file %s exists already. Use --force to recreate.", p);
7560fffc 1908
baed47c3 1909 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
9bff1410
LP
1910 SD_ID128_FORMAT_VAL(machine)) < 0)
1911 return log_oom();
7560fffc
LP
1912
1913 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
698cec65 1914 mpk = alloca_safe(mpk_size);
7560fffc
LP
1915
1916 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
698cec65 1917 seed = alloca_safe(seed_size);
7560fffc
LP
1918
1919 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
698cec65 1920 state = alloca_safe(state_size);
7560fffc 1921
7560fffc 1922 log_info("Generating seed...");
87cb1ab6 1923 r = crypto_random_bytes(seed, seed_size);
9bff1410
LP
1924 if (r < 0)
1925 return log_error_errno(r, "Failed to acquire random seed: %m");
7560fffc
LP
1926
1927 log_info("Generating key pair...");
1928 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
1929
baed47c3 1930 log_info("Generating sealing key...");
7560fffc
LP
1931 FSPRG_GenState0(state, mpk, seed, seed_size);
1932
baed47c3
LP
1933 assert(arg_interval > 0);
1934
7560fffc 1935 n = now(CLOCK_REALTIME);
baed47c3 1936 n /= arg_interval;
7560fffc 1937
03e334a1 1938 safe_close(fd);
646853bd 1939 fd = mkostemp_safe(k);
9bff1410
LP
1940 if (fd < 0)
1941 return log_error_errno(fd, "Failed to open %s: %m", k);
7560fffc 1942
c1631ee1
LP
1943 r = chattr_secret(fd, CHATTR_WARN_UNSUPPORTED_FLAGS);
1944 if (r < 0)
1945 log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
1946 r, "Failed to set file attributes on '%s', ignoring: %m", k);
f982e6f7 1947
41ab8c67 1948 struct FSSHeader h = {
9bff1410 1949 .signature = { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' },
41ab8c67
LP
1950 .machine_id = machine,
1951 .boot_id = boot,
1952 .header_size = htole64(sizeof(h)),
1953 .start_usec = htole64(n * arg_interval),
1954 .interval_usec = htole64(arg_interval),
1955 .fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR),
1956 .fsprg_state_size = htole64(state_size),
1957 };
1958
553acb7b 1959 r = loop_write(fd, &h, sizeof(h), false);
9bff1410
LP
1960 if (r < 0)
1961 return log_error_errno(r, "Failed to write header: %m");
7560fffc 1962
553acb7b 1963 r = loop_write(fd, state, state_size, false);
9bff1410
LP
1964 if (r < 0)
1965 return log_error_errno(r, "Failed to write state: %m");
7560fffc 1966
9bff1410
LP
1967 if (rename(k, p) < 0)
1968 return log_error_errno(errno, "Failed to link file: %m");
1969
1970 k = mfree(k);
7560fffc 1971
f1b82359
ZJS
1972 _cleanup_free_ char *hn = NULL, *key = NULL;
1973
1974 r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, false, &key);
1975 if (r < 0)
1976 return r;
da012db0 1977
8481248b 1978 if (on_tty()) {
da012db0
ZJS
1979 hn = gethostname_malloc();
1980 if (hn)
1981 hostname_cleanup(hn);
1982
7560fffc 1983 fprintf(stderr,
da012db0 1984 "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
7560fffc 1985 "\n"
da012db0
ZJS
1986 "The %ssecret sealing key%s has been written to the following local file.\n"
1987 "This key file is automatically updated when the sealing key is advanced.\n"
1988 "It should not be used on multiple hosts.\n"
7560fffc
LP
1989 "\n"
1990 "\t%s\n"
1991 "\n"
da012db0
ZJS
1992 "The sealing key is automatically changed every %s.\n"
1993 "\n"
54f8c958 1994 "Please write down the following %ssecret verification key%s. It should be stored\n"
da012db0 1995 "in a safe location and should not be saved locally on disk.\n"
54f8c958 1996 "\n\t%s",
d7a0f1f4
FS
1997 strempty(hn), hn ? "/" : "",
1998 SD_ID128_FORMAT_VAL(machine),
54f8c958 1999 ansi_highlight(), ansi_normal(),
9ea78383 2000 p,
5291f26d 2001 FORMAT_TIMESPAN(arg_interval, 0),
54f8c958 2002 ansi_highlight(), ansi_normal(),
9ea78383 2003 ansi_highlight_red());
7560fffc
LP
2004 fflush(stderr);
2005 }
da012db0 2006
f1b82359 2007 puts(key);
baed47c3 2008
8481248b 2009 if (on_tty()) {
da012db0 2010 fprintf(stderr, "%s", ansi_normal());
349cc4a5 2011#if HAVE_QRENCODE
f1b82359
ZJS
2012 _cleanup_free_ char *url = NULL;
2013 r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, true, &url);
2014 if (r < 0)
2015 return r;
2016
2017 (void) print_qrcode(stderr,
2018 "To transfer the verification key to your phone scan the QR code below",
2019 url);
f6a971bc 2020#endif
baed47c3 2021 }
7560fffc 2022
9bff1410 2023 return 0;
7560fffc 2024#else
baaa35ad
ZJS
2025 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
2026 "Forward-secure sealing not available.");
7560fffc
LP
2027#endif
2028}
2029
beec0085
LP
2030static int verify(sd_journal *j) {
2031 int r = 0;
beec0085
LP
2032 JournalFile *f;
2033
2034 assert(j);
2035
cedb42bb
LP
2036 log_show_color(true);
2037
90e74a66 2038 ORDERED_HASHMAP_FOREACH(f, j->files) {
beec0085 2039 int k;
a7f7d1bd 2040 usec_t first = 0, validated = 0, last = 0;
beec0085 2041
349cc4a5 2042#if HAVE_GCRYPT
feb12d3e 2043 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 2044 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 2045#endif
4da416aa 2046
2a7b539a 2047 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
fed66db0 2048 if (k == -EINVAL)
baed47c3 2049 /* If the key was invalid give up right-away. */
56e81f7c 2050 return k;
fed66db0
YW
2051 else if (k < 0)
2052 r = log_warning_errno(k, "FAIL: %s (%m)", f->path);
2053 else {
5291f26d 2054 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
beec0085 2055 log_info("PASS: %s", f->path);
6c7be122 2056
c0ca7aee 2057 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 2058 if (validated > 0) {
c0ca7aee 2059 log_info("=> Validated from %s to %s, final %s entries not sealed.",
5ab99e07
LP
2060 format_timestamp_maybe_utc(a, sizeof(a), first),
2061 format_timestamp_maybe_utc(b, sizeof(b), validated),
5291f26d 2062 FORMAT_TIMESPAN(last > validated ? last - validated : 0, 0));
2a7b539a 2063 } else if (last > 0)
c0ca7aee 2064 log_info("=> No sealing yet, %s of entries not sealed.",
5291f26d 2065 FORMAT_TIMESPAN(last - first, 0));
c0ca7aee
LP
2066 else
2067 log_info("=> No sealing yet, no entries in file.");
2068 }
6c7be122 2069 }
beec0085
LP
2070 }
2071
2072 return r;
2073}
2074
4f413af2
LP
2075static int simple_varlink_call(const char *option, const char *method) {
2076 _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
6b25db87 2077 const char *error, *fn;
74055aa7
LP
2078 int r;
2079
4f413af2
LP
2080 if (arg_machine)
2081 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option);
74055aa7 2082
6b25db87
LP
2083 fn = arg_namespace ?
2084 strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
2085 "/run/systemd/journal/io.systemd.journal";
2086
2087 r = varlink_connect_address(&link, fn);
94b65516 2088 if (r < 0)
6b25db87 2089 return log_error_errno(r, "Failed to connect to %s: %m", fn);
77740b59
ZJS
2090
2091 (void) varlink_set_description(link, "journal");
0bd3c210 2092 (void) varlink_set_relative_timeout(link, USEC_INFINITY);
74055aa7 2093
4f413af2
LP
2094 r = varlink_call(link, method, NULL, NULL, &error, NULL);
2095 if (r < 0)
17087340
YW
2096 return log_error_errno(r, "Failed to execute varlink call: %m");
2097 if (error)
2098 return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
2099 "Failed to execute varlink call: %s", error);
74055aa7 2100
4f413af2 2101 return 0;
74055aa7
LP
2102}
2103
4f413af2 2104static int flush_to_var(void) {
f6fca35e
FB
2105 if (access("/run/systemd/journal/flushed", F_OK) >= 0)
2106 return 0; /* Already flushed, no need to contact journald */
2107 if (errno != ENOENT)
2108 return log_error_errno(errno, "Unable to check for existence of /run/systemd/journal/flushed: %m");
2109
4f413af2 2110 return simple_varlink_call("--flush", "io.systemd.Journal.FlushToVar");
94b65516
LP
2111}
2112
c0dfcb31 2113static int relinquish_var(void) {
f2083c71 2114 return simple_varlink_call("--relinquish-var/--smart-relinquish-var", "io.systemd.Journal.RelinquishVar");
c0dfcb31
LP
2115}
2116
dbd6e31c 2117static int rotate(void) {
4f413af2 2118 return simple_varlink_call("--rotate", "io.systemd.Journal.Rotate");
dbd6e31c
LP
2119}
2120
2121static int sync_journal(void) {
4f413af2 2122 return simple_varlink_call("--sync", "io.systemd.Journal.Synchronize");
dbd6e31c
LP
2123}
2124
2a1e0f22
LP
2125static int wait_for_change(sd_journal *j, int poll_fd) {
2126 struct pollfd pollfds[] = {
2127 { .fd = poll_fd, .events = POLLIN },
2128 { .fd = STDOUT_FILENO },
2129 };
2a1e0f22 2130 usec_t timeout;
a963990f 2131 int r;
2a1e0f22
LP
2132
2133 assert(j);
2134 assert(poll_fd >= 0);
2135
2136 /* Much like sd_journal_wait() but also keeps an eye on STDOUT, and exits as soon as we see a POLLHUP on that,
2137 * i.e. when it is closed. */
2138
2139 r = sd_journal_get_timeout(j, &timeout);
2140 if (r < 0)
2141 return log_error_errno(r, "Failed to determine journal waiting time: %m");
2142
d9e2af0a
YW
2143 r = ppoll_usec(pollfds, ELEMENTSOF(pollfds), timeout);
2144 if (r == -EINTR)
2145 return 0;
2146 if (r < 0)
2147 return log_error_errno(r, "Couldn't wait for journal event: %m");
2a1e0f22 2148
d9e2af0a 2149 if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
baaa35ad
ZJS
2150 return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
2151 "Standard output has been closed.");
2a1e0f22
LP
2152
2153 r = sd_journal_process(j);
2154 if (r < 0)
2155 return log_error_errno(r, "Failed to process journal events: %m");
2156
2157 return 0;
2158}
2159
2160int main(int argc, char *argv[]) {
cc171228
LP
2161 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2162 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2163 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
2a1e0f22 2164 bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
d9e15cbd 2165 bool use_cursor = false, after_cursor = false;
4afd3348 2166 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
55ce4e1b 2167 sd_id128_t previous_boot_id = {}; /* Unnecessary initialization to appease gcc */
2a1e0f22 2168 int n_shown = 0, r, poll_fd = -1;
a963990f 2169
a9cdc94f 2170 setlocale(LC_ALL, "");
d2acb93d 2171 log_setup();
a963990f 2172
1abaf488
LP
2173 /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
2174 * split up into many files. */
2175 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
2176
a963990f
LP
2177 r = parse_argv(argc, argv);
2178 if (r <= 0)
2179 goto finish;
2180
cc171228
LP
2181 if (arg_image) {
2182 assert(!arg_root);
2183
2184 r = mount_image_privately_interactively(
2185 arg_image,
4b5de5dd
LP
2186 DISSECT_IMAGE_GENERIC_ROOT |
2187 DISSECT_IMAGE_REQUIRE_ROOT |
2188 DISSECT_IMAGE_VALIDATE_OS |
2189 DISSECT_IMAGE_RELAX_VAR_CHECK |
c65f854a 2190 (arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY),
cc171228
LP
2191 &unlink_dir,
2192 &loop_device,
2193 &decrypted_image);
2194 if (r < 0)
2195 return r;
2196
2197 arg_root = strdup(unlink_dir);
2198 if (!arg_root)
2199 return log_oom();
2200 }
2201
ed757c0c 2202 signal(SIGWINCH, columns_lines_cache_reset);
2cf4172a 2203 sigbus_install();
ed757c0c 2204
a020b3b3 2205 switch (arg_action) {
94b65516 2206
a020b3b3 2207 case ACTION_NEW_ID128:
a19fdd66 2208 r = id128_print_new(ID128_PRINT_PRETTY);
e3fdfb49 2209 goto finish;
e3fdfb49 2210
a020b3b3 2211 case ACTION_SETUP_KEYS:
7560fffc
LP
2212 r = setup_keys();
2213 goto finish;
844ec79b 2214
a020b3b3
LP
2215 case ACTION_LIST_CATALOG:
2216 case ACTION_DUMP_CATALOG:
2217 case ACTION_UPDATE_CATALOG: {
c2b2df60 2218 _cleanup_free_ char *database = NULL;
0c6ea3a4 2219
652ef298 2220 database = path_join(arg_root, CATALOG_DATABASE);
0c6ea3a4
ZJS
2221 if (!database) {
2222 r = log_oom();
2223 goto finish;
13cbf3a5
ZJS
2224 }
2225
844ec79b 2226 if (arg_action == ACTION_UPDATE_CATALOG) {
13cbf3a5 2227 r = catalog_update(database, arg_root, catalog_file_dirs);
844ec79b 2228 if (r < 0)
da927ba9 2229 log_error_errno(r, "Failed to list catalog: %m");
844ec79b
ZJS
2230 } else {
2231 bool oneline = arg_action == ACTION_LIST_CATALOG;
2232
384c2c32 2233 pager_open(arg_pager_flags);
a020b3b3 2234
844ec79b 2235 if (optind < argc)
a020b3b3 2236 r = catalog_list_items(stdout, database, oneline, argv + optind);
844ec79b 2237 else
13cbf3a5 2238 r = catalog_list(stdout, database, oneline);
844ec79b 2239 if (r < 0)
da927ba9 2240 log_error_errno(r, "Failed to list catalog: %m");
844ec79b 2241 }
d4205751 2242
d4205751
LP
2243 goto finish;
2244 }
2245
a020b3b3
LP
2246 case ACTION_FLUSH:
2247 r = flush_to_var();
2248 goto finish;
2249
c0dfcb31
LP
2250 case ACTION_RELINQUISH_VAR:
2251 r = relinquish_var();
2252 goto finish;
2253
a020b3b3
LP
2254 case ACTION_SYNC:
2255 r = sync_journal();
2256 goto finish;
2257
2258 case ACTION_ROTATE:
2259 r = rotate();
2260 goto finish;
2261
2262 case ACTION_SHOW:
2263 case ACTION_PRINT_HEADER:
2264 case ACTION_VERIFY:
2265 case ACTION_DISK_USAGE:
2266 case ACTION_LIST_BOOTS:
2267 case ACTION_VACUUM:
8df64fd0 2268 case ACTION_ROTATE_AND_VACUUM:
69e714f3
LP
2269 case ACTION_LIST_FIELDS:
2270 case ACTION_LIST_FIELD_NAMES:
a020b3b3
LP
2271 /* These ones require access to the journal files, continue below. */
2272 break;
2273
2274 default:
04499a70 2275 assert_not_reached();
a020b3b3
LP
2276 }
2277
a963990f 2278 if (arg_directory)
3f3a438f 2279 r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
0a175093
ZJS
2280 else if (arg_root)
2281 r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
33ff7464
LP
2282 else if (arg_file_stdin)
2283 r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0);
2284 else if (arg_file)
8d98da3f 2285 r = sd_journal_open_files(&j, (const char**) arg_file, 0);
d38c62cc
LP
2286 else if (arg_machine) {
2287 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2288 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2289 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2290 int fd;
2291
2292 if (geteuid() != 0) {
2293 /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
2294 * the container, thus we need root privileges to override them. */
0491150b 2295 r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Using the --machine= switch requires root privileges.");
d38c62cc
LP
2296 goto finish;
2297 }
2298
2299 r = sd_bus_open_system(&bus);
2300 if (r < 0) {
2301 log_error_errno(r, "Failed to open system bus: %m");
2302 goto finish;
2303 }
2304
2305 r = sd_bus_call_method(
2306 bus,
2307 "org.freedesktop.machine1",
2308 "/org/freedesktop/machine1",
2309 "org.freedesktop.machine1.Manager",
2310 "OpenMachineRootDirectory",
2311 &error,
2312 &reply,
2313 "s", arg_machine);
2314 if (r < 0) {
2315 log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r));
2316 goto finish;
2317 }
2318
2319 r = sd_bus_message_read(reply, "h", &fd);
2320 if (r < 0) {
2321 bus_log_parse_error(r);
2322 goto finish;
2323 }
2324
2325 fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
2326 if (fd < 0) {
2327 r = log_error_errno(errno, "Failed to duplicate file descriptor: %m");
2328 goto finish;
2329 }
2330
2331 r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT);
2332 if (r < 0)
2333 safe_close(fd);
2334 } else
6b25db87
LP
2335 r = sd_journal_open_namespace(
2336 &j,
2337 arg_namespace,
2338 (arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
2339 arg_namespace_flags | arg_journal_type);
a963990f 2340 if (r < 0) {
a020b3b3 2341 log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
909dea0c 2342 goto finish;
a963990f
LP
2343 }
2344
e79d0b59
ZJS
2345 r = journal_access_check_and_warn(j, arg_quiet,
2346 !(arg_journal_type == SD_JOURNAL_CURRENT_USER || arg_user_units));
6fe391c5 2347 if (r < 0)
909dea0c 2348 goto finish;
6fe391c5 2349
a020b3b3 2350 switch (arg_action) {
beec0085 2351
a020b3b3
LP
2352 case ACTION_NEW_ID128:
2353 case ACTION_SETUP_KEYS:
2354 case ACTION_LIST_CATALOG:
2355 case ACTION_DUMP_CATALOG:
2356 case ACTION_UPDATE_CATALOG:
2357 case ACTION_FLUSH:
2358 case ACTION_SYNC:
2359 case ACTION_ROTATE:
04499a70 2360 assert_not_reached();
a020b3b3
LP
2361
2362 case ACTION_PRINT_HEADER:
dca6219e 2363 journal_print_header(j);
909dea0c
LP
2364 r = 0;
2365 goto finish;
dca6219e 2366
a020b3b3
LP
2367 case ACTION_VERIFY:
2368 r = verify(j);
2369 goto finish;
2370
2371 case ACTION_DISK_USAGE: {
39883f62 2372 uint64_t bytes = 0;
a1a03e30
LP
2373
2374 r = sd_journal_get_usage(j, &bytes);
2375 if (r < 0)
909dea0c 2376 goto finish;
a1a03e30 2377
8da830bc 2378 printf("Archived and active journals take up %s in the file system.\n",
2b59bf51 2379 FORMAT_BYTES(bytes));
909dea0c 2380 goto finish;
a1a03e30
LP
2381 }
2382
a020b3b3
LP
2383 case ACTION_LIST_BOOTS:
2384 r = list_boots(j);
2385 goto finish;
2386
8df64fd0
LP
2387 case ACTION_ROTATE_AND_VACUUM:
2388
2389 r = rotate();
2390 if (r < 0)
2391 goto finish;
2392
2393 _fallthrough_;
2394
a020b3b3 2395 case ACTION_VACUUM: {
dbd2a83f 2396 Directory *d;
dbd2a83f 2397
90e74a66 2398 HASHMAP_FOREACH(d, j->directories_by_path) {
dbd2a83f
LP
2399 int q;
2400
e3695e49 2401 q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, !arg_quiet);
fed66db0
YW
2402 if (q < 0)
2403 r = log_error_errno(q, "Failed to vacuum %s: %m", d->path);
dbd2a83f
LP
2404 }
2405
909dea0c 2406 goto finish;
dbd2a83f
LP
2407 }
2408
69e714f3
LP
2409 case ACTION_LIST_FIELD_NAMES: {
2410 const char *field;
2411
2412 SD_JOURNAL_FOREACH_FIELD(j, field) {
2413 printf("%s\n", field);
313cefa1 2414 n_shown++;
69e714f3
LP
2415 }
2416
2417 r = 0;
2418 goto finish;
2419 }
2420
a020b3b3 2421 case ACTION_SHOW:
69e714f3 2422 case ACTION_LIST_FIELDS:
a020b3b3
LP
2423 break;
2424
2425 default:
04499a70 2426 assert_not_reached();
f1188074
ZJS
2427 }
2428
0f1a9a83
JS
2429 if (arg_boot_offset != 0 &&
2430 sd_journal_has_runtime_files(j) > 0 &&
2431 sd_journal_has_persistent_files(j) == 0) {
493097ee 2432 log_info("Specifying boot ID or boot offset has no effect, no persistent journal was found.");
0f1a9a83
JS
2433 r = 0;
2434 goto finish;
2435 }
a331b5e6
JJ
2436 /* add_boot() must be called first!
2437 * It may need to seek the journal to find parent boot IDs. */
2438 r = add_boot(j);
a963990f 2439 if (r < 0)
909dea0c 2440 goto finish;
a963990f 2441
99271804
ZJS
2442 r = add_dmesg(j);
2443 if (r < 0)
909dea0c 2444 goto finish;
99271804 2445
b9e40524 2446 r = add_units(j);
ea18a4b5 2447 if (r < 0) {
da927ba9 2448 log_error_errno(r, "Failed to add filter for units: %m");
909dea0c 2449 goto finish;
ea18a4b5 2450 }
c3f60ec5 2451
73083640
HH
2452 r = add_syslog_identifier(j);
2453 if (r < 0) {
da927ba9 2454 log_error_errno(r, "Failed to add filter for syslog identifiers: %m");
909dea0c 2455 goto finish;
73083640
HH
2456 }
2457
cd34b3c6 2458 r = add_priorities(j);
b56d608e 2459 if (r < 0)
909dea0c 2460 goto finish;
a963990f 2461
196dedd5
ZJS
2462 r = add_facilities(j);
2463 if (r < 0)
2464 goto finish;
2465
cd34b3c6 2466 r = add_matches(j, argv + optind);
b56d608e 2467 if (r < 0)
909dea0c 2468 goto finish;
941e990d 2469
f1d34068 2470 if (DEBUG_LOGGING) {
c2b2df60 2471 _cleanup_free_ char *filter = NULL;
4ad16808
ZJS
2472
2473 filter = journal_make_match_string(j);
b56d608e
LP
2474 if (!filter)
2475 return log_oom();
2476
4ad16808
ZJS
2477 log_debug("Journal filter: %s", filter);
2478 }
67e04a48 2479
69e714f3 2480 if (arg_action == ACTION_LIST_FIELDS) {
15119c16
LP
2481 const void *data;
2482 size_t size;
2483
69e714f3
LP
2484 assert(arg_field);
2485
21ae4593
ZJS
2486 r = sd_journal_set_data_threshold(j, 0);
2487 if (r < 0) {
b56d608e 2488 log_error_errno(r, "Failed to unset data size threshold: %m");
909dea0c 2489 goto finish;
21ae4593
ZJS
2490 }
2491
15119c16
LP
2492 r = sd_journal_query_unique(j, arg_field);
2493 if (r < 0) {
da927ba9 2494 log_error_errno(r, "Failed to query unique data objects: %m");
909dea0c 2495 goto finish;
15119c16
LP
2496 }
2497
2498 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
2499 const void *eq;
2500
67e04a48 2501 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
2502 break;
2503
15119c16
LP
2504 eq = memchr(data, '=', size);
2505 if (eq)
2506 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
2507 else
2508 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875 2509
313cefa1 2510 n_shown++;
15119c16
LP
2511 }
2512
909dea0c
LP
2513 r = 0;
2514 goto finish;
15119c16
LP
2515 }
2516
8d98da3f
ZJS
2517 /* Opening the fd now means the first sd_journal_wait() will actually wait */
2518 if (arg_follow) {
2a1e0f22
LP
2519 poll_fd = sd_journal_get_fd(j);
2520 if (poll_fd == -EMFILE) {
5238e957 2521 log_warning_errno(poll_fd, "Insufficient watch descriptors available. Reverting to -n.");
321ed364 2522 arg_follow = false;
2a1e0f22
LP
2523 } else if (poll_fd == -EMEDIUMTYPE) {
2524 log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN.");
5d1ce257 2525 goto finish;
2a1e0f22
LP
2526 } else if (poll_fd < 0) {
2527 log_error_errno(poll_fd, "Failed to get journal fd: %m");
909dea0c 2528 goto finish;
b56d608e 2529 }
8d98da3f
ZJS
2530 }
2531
d9e15cbd
JS
2532 if (arg_cursor || arg_after_cursor || arg_cursor_file) {
2533 _cleanup_free_ char *cursor_from_file = NULL;
2534 const char *cursor = arg_cursor ?: arg_after_cursor;
2535
2536 if (arg_cursor_file) {
2537 r = read_one_line_file(arg_cursor_file, &cursor_from_file);
2538 if (r < 0 && r != -ENOENT) {
2539 log_error_errno(r, "Failed to read cursor file %s: %m", arg_cursor_file);
2540 goto finish;
2541 }
2542
2543 if (r > 0) {
2544 cursor = cursor_from_file;
2545 after_cursor = true;
2546 }
2547 } else
d7a0f1f4 2548 after_cursor = arg_after_cursor;
d9e15cbd
JS
2549
2550 if (cursor) {
2551 r = sd_journal_seek_cursor(j, cursor);
2552 if (r < 0) {
2553 log_error_errno(r, "Failed to seek to cursor: %m");
2554 goto finish;
2555 }
2556 use_cursor = true;
08984293 2557 }
d9e15cbd 2558 }
909dea0c 2559
d9e15cbd 2560 if (use_cursor) {
d89d6c86 2561 if (!arg_reverse)
d9e15cbd 2562 r = sd_journal_next_skip(j, 1 + after_cursor);
d89d6c86 2563 else
d9e15cbd 2564 r = sd_journal_previous_skip(j, 1 + after_cursor);
248fc619 2565
d9e15cbd 2566 if (after_cursor && r < 2) {
248fc619 2567 /* We couldn't find the next entry after the cursor. */
8ee8e536
WD
2568 if (arg_follow)
2569 need_seek = true;
2570 else
2571 arg_lines = 0;
2572 }
08984293 2573
d89d6c86 2574 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 2575 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 2576 if (r < 0) {
da927ba9 2577 log_error_errno(r, "Failed to seek to date: %m");
909dea0c 2578 goto finish;
8f14c832 2579 }
8f14c832
LP
2580 r = sd_journal_next(j);
2581
d89d6c86
LN
2582 } else if (arg_until_set && arg_reverse) {
2583 r = sd_journal_seek_realtime_usec(j, arg_until);
2584 if (r < 0) {
da927ba9 2585 log_error_errno(r, "Failed to seek to date: %m");
909dea0c 2586 goto finish;
d89d6c86
LN
2587 }
2588 r = sd_journal_previous(j);
2589
23b39216 2590 } else if (arg_reverse) {
2100675e
LP
2591 r = sd_journal_seek_tail(j);
2592 if (r < 0) {
da927ba9 2593 log_error_errno(r, "Failed to seek to tail: %m");
909dea0c 2594 goto finish;
2100675e
LP
2595 }
2596
23b39216 2597 r = sd_journal_previous(j);
8f14c832 2598
23b39216 2599 } else if (arg_lines >= 0) {
d89d6c86
LN
2600 r = sd_journal_seek_tail(j);
2601 if (r < 0) {
da927ba9 2602 log_error_errno(r, "Failed to seek to tail: %m");
909dea0c 2603 goto finish;
d89d6c86
LN
2604 }
2605
23b39216 2606 r = sd_journal_previous_skip(j, arg_lines);
d89d6c86 2607
2100675e
LP
2608 } else {
2609 r = sd_journal_seek_head(j);
2610 if (r < 0) {
da927ba9 2611 log_error_errno(r, "Failed to seek to head: %m");
909dea0c 2612 goto finish;
2100675e 2613 }
6f003b43
LP
2614
2615 r = sd_journal_next(j);
2616 }
2617
2618 if (r < 0) {
da927ba9 2619 log_error_errno(r, "Failed to iterate through journal: %m");
909dea0c 2620 goto finish;
50f20cfd 2621 }
5f42943c
LK
2622 if (r == 0)
2623 need_seek = true;
87d2c1ff 2624
faf5077f 2625 if (!arg_follow)
384c2c32 2626 pager_open(arg_pager_flags);
0d43c694 2627
a2d7654f 2628 if (!arg_quiet && (arg_lines != 0 || arg_follow) && DEBUG_LOGGING) {
cfbc22ab
LP
2629 usec_t start, end;
2630 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
2631
2632 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
2633 if (r < 0) {
da927ba9 2634 log_error_errno(r, "Failed to get cutoff: %m");
cfbc22ab
LP
2635 goto finish;
2636 }
2637
2638 if (r > 0) {
2639 if (arg_follow)
b91ae210 2640 printf("-- Journal begins at %s. --\n",
5ab99e07 2641 format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start));
cfbc22ab 2642 else
b91ae210 2643 printf("-- Journal begins at %s, ends at %s. --\n",
5ab99e07
LP
2644 format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start),
2645 format_timestamp_maybe_utc(end_buf, sizeof(end_buf), end));
cfbc22ab
LP
2646 }
2647 }
2648
50f20cfd 2649 for (;;) {
67e04a48 2650 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab 2651 int flags;
b4766d5f 2652 size_t highlight[2] = {};
cfbc22ab 2653
6f003b43 2654 if (need_seek) {
99613ec5 2655 if (!arg_reverse)
d89d6c86
LN
2656 r = sd_journal_next(j);
2657 else
2658 r = sd_journal_previous(j);
6f003b43 2659 if (r < 0) {
da927ba9 2660 log_error_errno(r, "Failed to iterate through journal: %m");
6f003b43
LP
2661 goto finish;
2662 }
a72b6353
ZJS
2663 if (r == 0)
2664 break;
0d43c694
LP
2665 }
2666
d89d6c86 2667 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
2668 usec_t usec;
2669
2670 r = sd_journal_get_realtime_usec(j, &usec);
2671 if (r < 0) {
da927ba9 2672 log_error_errno(r, "Failed to determine timestamp: %m");
cfbc22ab
LP
2673 goto finish;
2674 }
3ba09ee8 2675 if (usec > arg_until)
3ac9cac7 2676 break;
cfbc22ab
LP
2677 }
2678
d89d6c86
LN
2679 if (arg_since_set && arg_reverse) {
2680 usec_t usec;
2681
2682 r = sd_journal_get_realtime_usec(j, &usec);
2683 if (r < 0) {
da927ba9 2684 log_error_errno(r, "Failed to determine timestamp: %m");
d89d6c86
LN
2685 goto finish;
2686 }
2687 if (usec < arg_since)
3ac9cac7 2688 break;
d89d6c86
LN
2689 }
2690
4bed2485 2691 if (!arg_merge && !arg_quiet) {
cd931c0a 2692 sd_id128_t boot_id;
14a65d65 2693
cd931c0a
LP
2694 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
2695 if (r >= 0) {
2696 if (previous_boot_id_valid &&
2697 !sd_id128_equal(boot_id, previous_boot_id))
ea394d48
RP
2698 printf("%s-- Boot "SD_ID128_FORMAT_STR" --%s\n",
2699 ansi_highlight(), SD_ID128_FORMAT_VAL(boot_id), ansi_normal());
cd931c0a
LP
2700
2701 previous_boot_id = boot_id;
2702 previous_boot_id_valid = true;
2703 }
14a65d65
LP
2704 }
2705
6becf48c 2706#if HAVE_PCRE2
61c5f8a1 2707 if (arg_compiled_pattern) {
9200bb30 2708 _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
6becf48c
ZJS
2709 const void *message;
2710 size_t len;
b4766d5f 2711 PCRE2_SIZE *ovec;
6becf48c 2712
9200bb30 2713 md = sym_pcre2_match_data_create(1, NULL);
6becf48c
ZJS
2714 if (!md)
2715 return log_oom();
2716
2717 r = sd_journal_get_data(j, "MESSAGE", &message, &len);
2718 if (r < 0) {
2719 if (r == -ENOENT) {
2720 need_seek = true;
2721 continue;
2722 }
2723
2724 log_error_errno(r, "Failed to get MESSAGE field: %m");
2725 goto finish;
2726 }
2727
2728 assert_se(message = startswith(message, "MESSAGE="));
2729
9200bb30
LP
2730 r = sym_pcre2_match(arg_compiled_pattern,
2731 message,
2732 len - strlen("MESSAGE="),
2733 0, /* start at offset 0 in the subject */
2734 0, /* default options */
2735 md,
2736 NULL);
6becf48c
ZJS
2737 if (r == PCRE2_ERROR_NOMATCH) {
2738 need_seek = true;
2739 continue;
2740 }
2741 if (r < 0) {
2742 unsigned char buf[LINE_MAX];
2743 int r2;
2744
9200bb30 2745 r2 = sym_pcre2_get_error_message(r, buf, sizeof buf);
6becf48c
ZJS
2746 log_error("Pattern matching failed: %s",
2747 r2 < 0 ? "unknown error" : (char*) buf);
2748 r = -EINVAL;
2749 goto finish;
2750 }
b4766d5f 2751
9200bb30 2752 ovec = sym_pcre2_get_ovector_pointer(md);
b4766d5f
ZJS
2753 highlight[0] = ovec[0];
2754 highlight[1] = ovec[1];
6becf48c
ZJS
2755 }
2756#endif
2757
cfbc22ab 2758 flags =
cd4b13e0 2759 arg_all * OUTPUT_SHOW_ALL |
2b8f6883 2760 arg_full * OUTPUT_FULL_WIDTH |
40c9fe4c 2761 colors_enabled() * OUTPUT_COLOR |
9fd29044 2762 arg_catalog * OUTPUT_CATALOG |
991e274b
LP
2763 arg_utc * OUTPUT_UTC |
2764 arg_no_hostname * OUTPUT_NO_HOSTNAME;
cfbc22ab 2765
9b972c9a
ZJS
2766 r = show_journal_entry(stdout, j, arg_output, 0, flags,
2767 arg_output_fields, highlight, &ellipsized);
a72b6353
ZJS
2768 need_seek = true;
2769 if (r == -EADDRNOTAVAIL)
2770 break;
2a1e0f22 2771 else if (r < 0)
72f59706 2772 goto finish;
6f003b43 2773
cfbc22ab 2774 n_shown++;
ec316d19
PP
2775
2776 /* If journalctl take a long time to process messages, and during that time journal file
2777 * rotation occurs, a journalctl client will keep those rotated files open until it calls
2778 * sd_journal_process(), which typically happens as a result of calling sd_journal_wait() below
2779 * in the "following" case. By periodically calling sd_journal_process() during the processing
2780 * loop we shrink the window of time a client instance has open file descriptors for rotated
2781 * (deleted) journal files. */
2782 if ((n_shown % PROCESS_INOTIFY_INTERVAL) == 0) {
2783 r = sd_journal_process(j);
2784 if (r < 0) {
2785 log_error_errno(r, "Failed to process inotify events: %m");
2786 goto finish;
2787 }
2788 }
87d2c1ff
LP
2789 }
2790
248fc619 2791 if (!arg_follow) {
5f42943c
LK
2792 if (n_shown == 0 && !arg_quiet)
2793 printf("-- No entries --\n");
50f20cfd 2794 break;
248fc619 2795 }
50f20cfd 2796
b1aa5ced 2797 fflush(stdout);
2a1e0f22
LP
2798
2799 r = wait_for_change(j, poll_fd);
2800 if (r < 0)
50f20cfd 2801 goto finish;
67e04a48
ZJS
2802
2803 first_line = false;
de190aef 2804 }
87d2c1ff 2805
3ac9cac7
TS
2806 if (arg_show_cursor || arg_cursor_file) {
2807 _cleanup_free_ char *cursor = NULL;
2808
2809 r = sd_journal_get_cursor(j, &cursor);
2810 if (r < 0 && r != -EADDRNOTAVAIL)
2811 log_error_errno(r, "Failed to get cursor: %m");
2812 else if (r >= 0) {
2813 if (arg_show_cursor)
2814 printf("-- cursor: %s\n", cursor);
2815
2816 if (arg_cursor_file) {
2817 r = write_string_file(arg_cursor_file, cursor,
2818 WRITE_STRING_FILE_CREATE |
2819 WRITE_STRING_FILE_ATOMIC);
2820 if (r < 0)
2821 log_error_errno(r,
2822 "Failed to write new cursor to %s: %m",
2823 arg_cursor_file);
2824 }
2825 }
2826 }
2827
87d2c1ff 2828finish:
0d43c694
LP
2829 pager_close();
2830
6becf48c 2831#if HAVE_PCRE2
2e64b27a 2832 if (arg_compiled_pattern && r == 0 && n_shown == 0)
6cda6774
FS
2833 /* --grep was used, no error was thrown, but the pattern didn't
2834 * match anything. Let's mimic grep's behavior here and return
2835 * a non-zero exit code, so journalctl --grep can be used
2836 * in scripts and such */
2e64b27a 2837 r = -ENOENT;
6becf48c
ZJS
2838#endif
2839
3fbf9cbb 2840 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 2841}