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