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