]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
95b6cf20dae1c7aa8ff0f65527fa5b6ef9e6c19f
[thirdparty/systemd.git] / src / journal / journalctl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <locale.h>
5
6 #include "sd-journal.h"
7
8 #include "build.h"
9 #include "dissect-image.h"
10 #include "extract-word.h"
11 #include "glob-util.h"
12 #include "id128-print.h"
13 #include "image-policy.h"
14 #include "journalctl.h"
15 #include "journalctl-authenticate.h"
16 #include "journalctl-catalog.h"
17 #include "journalctl-misc.h"
18 #include "journalctl-show.h"
19 #include "journalctl-varlink.h"
20 #include "log.h"
21 #include "loop-util.h"
22 #include "main-func.h"
23 #include "mount-util.h"
24 #include "mountpoint-util.h"
25 #include "output-mode.h"
26 #include "pager.h"
27 #include "parse-argument.h"
28 #include "parse-util.h"
29 #include "pcre2-util.h"
30 #include "pretty-print.h"
31 #include "set.h"
32 #include "static-destruct.h"
33 #include "string-table.h"
34 #include "string-util.h"
35 #include "strv.h"
36 #include "syslog-util.h"
37 #include "time-util.h"
38
39 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
40
41 enum {
42 /* Special values for arg_lines */
43 ARG_LINES_DEFAULT = -2,
44 ARG_LINES_ALL = -1,
45 };
46
47 JournalctlAction arg_action = ACTION_SHOW;
48 OutputMode arg_output = OUTPUT_SHORT;
49 sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
50 PagerFlags arg_pager_flags = 0;
51 bool arg_utc = false;
52 bool arg_follow = false;
53 bool arg_full = true;
54 bool arg_all = false;
55 int arg_lines = ARG_LINES_DEFAULT;
56 bool arg_lines_oldest = false;
57 bool arg_no_tail = false;
58 bool arg_truncate_newline = false;
59 bool arg_quiet = false;
60 bool arg_merge = false;
61 int arg_boot = -1; /* tristate */
62 sd_id128_t arg_boot_id = {};
63 int arg_boot_offset = 0;
64 bool arg_dmesg = false;
65 bool arg_no_hostname = false;
66 char *arg_cursor = NULL;
67 char *arg_cursor_file = NULL;
68 char *arg_after_cursor = NULL;
69 bool arg_show_cursor = false;
70 char *arg_directory = NULL;
71 char **arg_file = NULL;
72 bool arg_file_stdin = false;
73 int arg_priorities = 0;
74 Set *arg_facilities = NULL;
75 char *arg_verify_key = NULL;
76 #if HAVE_GCRYPT
77 usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
78 bool arg_force = false;
79 #endif
80 usec_t arg_since = 0;
81 usec_t arg_until = 0;
82 bool arg_since_set = false;
83 bool arg_until_set = false;
84 char **arg_syslog_identifier = NULL;
85 char **arg_exclude_identifier = NULL;
86 char **arg_system_units = NULL;
87 char **arg_user_units = NULL;
88 bool arg_invocation = false;
89 sd_id128_t arg_invocation_id = SD_ID128_NULL;
90 int arg_invocation_offset = 0;
91 char *arg_field = NULL;
92 bool arg_catalog = false;
93 bool arg_reverse = false;
94 int arg_journal_type = 0;
95 int arg_journal_additional_open_flags = 0;
96 int arg_namespace_flags = 0;
97 char *arg_root = NULL;
98 char *arg_image = NULL;
99 char *arg_machine = NULL;
100 char *arg_namespace = NULL;
101 uint64_t arg_vacuum_size = 0;
102 uint64_t arg_vacuum_n_files = 0;
103 usec_t arg_vacuum_time = 0;
104 Set *arg_output_fields = NULL;
105 char *arg_pattern = NULL;
106 pcre2_code *arg_compiled_pattern = NULL;
107 PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
108 ImagePolicy *arg_image_policy = NULL;
109 bool arg_synchronize_on_exit = false;
110
111 STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
112 STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
113 STATIC_DESTRUCTOR_REGISTER(arg_after_cursor, freep);
114 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
115 STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
116 STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
117 STATIC_DESTRUCTOR_REGISTER(arg_verify_key, erase_and_freep);
118 STATIC_DESTRUCTOR_REGISTER(arg_syslog_identifier, strv_freep);
119 STATIC_DESTRUCTOR_REGISTER(arg_exclude_identifier, strv_freep);
120 STATIC_DESTRUCTOR_REGISTER(arg_system_units, strv_freep);
121 STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
122 STATIC_DESTRUCTOR_REGISTER(arg_field, freep);
123 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
124 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
125 STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
126 STATIC_DESTRUCTOR_REGISTER(arg_namespace, freep);
127 STATIC_DESTRUCTOR_REGISTER(arg_output_fields, set_freep);
128 STATIC_DESTRUCTOR_REGISTER(arg_pattern, freep);
129 STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pcre2_code_freep);
130 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
131
132 static int parse_id_descriptor(const char *x, sd_id128_t *ret_id, int *ret_offset) {
133 sd_id128_t id = SD_ID128_NULL;
134 int off = 0, r;
135
136 assert(x);
137 assert(ret_id);
138 assert(ret_offset);
139
140 if (streq(x, "all")) {
141 *ret_id = SD_ID128_NULL;
142 *ret_offset = 0;
143 return 0;
144 }
145
146 if (strlen(x) >= SD_ID128_STRING_MAX - 1) {
147 char *t;
148
149 t = strndupa_safe(x, SD_ID128_STRING_MAX - 1);
150 r = sd_id128_from_string(t, &id);
151 if (r >= 0)
152 x += SD_ID128_STRING_MAX - 1;
153
154 if (!IN_SET(*x, 0, '-', '+'))
155 return -EINVAL;
156
157 if (*x != 0) {
158 r = safe_atoi(x, &off);
159 if (r < 0)
160 return r;
161 }
162 } else {
163 r = safe_atoi(x, &off);
164 if (r < 0)
165 return r;
166 }
167
168 *ret_id = id;
169 *ret_offset = off;
170 return 1;
171 }
172
173 static int parse_lines(const char *arg, bool graceful) {
174 const char *l;
175 int n, r;
176
177 assert(arg || graceful);
178
179 if (!arg)
180 goto default_noarg;
181
182 if (streq(arg, "all")) {
183 arg_lines = ARG_LINES_ALL;
184 return 1;
185 }
186
187 l = startswith(arg, "+");
188
189 r = safe_atoi(l ?: arg, &n);
190 if (r < 0 || n < 0) {
191 if (graceful)
192 goto default_noarg;
193
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse --lines='%s'.", arg);
195 }
196
197 arg_lines = n;
198 arg_lines_oldest = l;
199
200 return 1;
201
202 default_noarg:
203 arg_lines = 10;
204 arg_lines_oldest = false;
205 return 0;
206 }
207
208 static int help_facilities(void) {
209 if (!arg_quiet)
210 puts("Available facilities:");
211
212 for (int i = 0; i < LOG_NFACILITIES; i++) {
213 _cleanup_free_ char *t = NULL;
214
215 if (log_facility_unshifted_to_string_alloc(i, &t) < 0)
216 return log_oom();
217 puts(t);
218 }
219
220 return 0;
221 }
222
223 static int help(void) {
224 _cleanup_free_ char *link = NULL;
225 int r;
226
227 pager_open(arg_pager_flags);
228
229 r = terminal_urlify_man("journalctl", "1", &link);
230 if (r < 0)
231 return log_oom();
232
233 printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
234 "%5$sQuery the journal.%6$s\n\n"
235 "%3$sSource Options:%4$s\n"
236 " --system Show the system journal\n"
237 " --user Show the user journal for the current user\n"
238 " -M --machine=CONTAINER Operate on local container\n"
239 " -m --merge Show entries from all available journals\n"
240 " -D --directory=PATH Show journal files from directory\n"
241 " -i --file=PATH Show journal file\n"
242 " --root=PATH Operate on an alternate filesystem root\n"
243 " --image=PATH Operate on disk image as filesystem root\n"
244 " --image-policy=POLICY Specify disk image dissection policy\n"
245 " --namespace=NAMESPACE Show journal data from specified journal namespace\n"
246 "\n%3$sFiltering Options:%4$s\n"
247 " -S --since=DATE Show entries not older than the specified date\n"
248 " -U --until=DATE Show entries not newer than the specified date\n"
249 " -c --cursor=CURSOR Show entries starting at the specified cursor\n"
250 " --after-cursor=CURSOR Show entries after the specified cursor\n"
251 " --cursor-file=FILE Show entries after cursor in FILE and update FILE\n"
252 " -b --boot[=ID] Show current boot or the specified boot\n"
253 " -u --unit=UNIT Show logs from the specified unit\n"
254 " --user-unit=UNIT Show logs from the specified user unit\n"
255 " --invocation=ID Show logs from the matching invocation ID\n"
256 " -I Show logs from the latest invocation of unit\n"
257 " -t --identifier=STRING Show entries with the specified syslog identifier\n"
258 " -T --exclude-identifier=STRING\n"
259 " Hide entries with the specified syslog identifier\n"
260 " -p --priority=RANGE Show entries within the specified priority range\n"
261 " --facility=FACILITY... Show entries with the specified facilities\n"
262 " -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
263 " --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
264 " -k --dmesg Show kernel message log from the current boot\n"
265 "\n%3$sOutput Control Options:%4$s\n"
266 " -o --output=STRING Change journal output mode (short, short-precise,\n"
267 " short-iso, short-iso-precise, short-full,\n"
268 " short-monotonic, short-unix, verbose, export,\n"
269 " json, json-pretty, json-sse, json-seq, cat,\n"
270 " with-unit)\n"
271 " --output-fields=LIST Select fields to print in verbose/export/json modes\n"
272 " -n --lines[=[+]INTEGER] Number of journal entries to show\n"
273 " -r --reverse Show the newest entries first\n"
274 " --show-cursor Print the cursor after all the entries\n"
275 " --utc Express time in Coordinated Universal Time (UTC)\n"
276 " -x --catalog Add message explanations where available\n"
277 " -W --no-hostname Suppress output of hostname field\n"
278 " --no-full Ellipsize fields\n"
279 " -a --all Show all fields, including long and unprintable\n"
280 " -f --follow Follow the journal\n"
281 " --no-tail Show all lines, even in follow mode\n"
282 " --truncate-newline Truncate entries by first newline character\n"
283 " -q --quiet Do not show info messages and privilege warning\n"
284 " --synchronize-on-exit=BOOL\n"
285 " Wait for Journal synchronization before exiting\n"
286 "\n%3$sPager Control Options:%4$s\n"
287 " --no-pager Do not pipe output into a pager\n"
288 " -e --pager-end Immediately jump to the end in the pager\n"
289 "\n%3$sForward Secure Sealing (FSS) Options:%4$s\n"
290 " --interval=TIME Time interval for changing the FSS sealing key\n"
291 " --verify-key=KEY Specify FSS verification key\n"
292 " --force Override of the FSS key pair with --setup-keys\n"
293 "\n%3$sCommands:%4$s\n"
294 " -h --help Show this help text\n"
295 " --version Show package version\n"
296 " -N --fields List all field names currently used\n"
297 " -F --field=FIELD List all values that a specified field takes\n"
298 " --list-boots Show terse information about recorded boots\n"
299 " --list-invocations Show invocation IDs of specified unit\n"
300 " --list-namespaces Show list of journal namespaces\n"
301 " --disk-usage Show total disk usage of all journal files\n"
302 " --vacuum-size=BYTES Reduce disk usage below specified size\n"
303 " --vacuum-files=INT Leave only the specified number of journal files\n"
304 " --vacuum-time=TIME Remove journal files older than specified time\n"
305 " --verify Verify journal file consistency\n"
306 " --sync Synchronize unwritten journal messages to disk\n"
307 " --relinquish-var Stop logging to disk, log to temporary file system\n"
308 " --smart-relinquish-var Similar, but NOP if log directory is on root mount\n"
309 " --flush Flush all journal data from /run into /var\n"
310 " --rotate Request immediate rotation of the journal files\n"
311 " --header Show journal header information\n"
312 " --list-catalog Show all message IDs in the catalog\n"
313 " --dump-catalog Show entries in the message catalog\n"
314 " --update-catalog Update the message catalog database\n"
315 " --setup-keys Generate a new FSS key pair\n"
316 "\nSee the %2$s for details.\n",
317 program_invocation_short_name,
318 link,
319 ansi_underline(),
320 ansi_normal(),
321 ansi_highlight(),
322 ansi_normal());
323
324 return 0;
325 }
326
327 static int parse_argv(int argc, char *argv[]) {
328
329 enum {
330 ARG_VERSION = 0x100,
331 ARG_NO_PAGER,
332 ARG_NO_FULL,
333 ARG_NO_TAIL,
334 ARG_NEW_ID128,
335 ARG_THIS_BOOT,
336 ARG_LIST_BOOTS,
337 ARG_LIST_INVOCATIONS,
338 ARG_USER,
339 ARG_SYSTEM,
340 ARG_ROOT,
341 ARG_IMAGE,
342 ARG_IMAGE_POLICY,
343 ARG_HEADER,
344 ARG_FACILITY,
345 ARG_SETUP_KEYS,
346 ARG_INTERVAL,
347 ARG_VERIFY,
348 ARG_VERIFY_KEY,
349 ARG_DISK_USAGE,
350 ARG_AFTER_CURSOR,
351 ARG_CURSOR_FILE,
352 ARG_SHOW_CURSOR,
353 ARG_USER_UNIT,
354 ARG_INVOCATION,
355 ARG_LIST_CATALOG,
356 ARG_DUMP_CATALOG,
357 ARG_UPDATE_CATALOG,
358 ARG_FORCE,
359 ARG_CASE_SENSITIVE,
360 ARG_UTC,
361 ARG_SYNC,
362 ARG_FLUSH,
363 ARG_RELINQUISH_VAR,
364 ARG_SMART_RELINQUISH_VAR,
365 ARG_ROTATE,
366 ARG_TRUNCATE_NEWLINE,
367 ARG_VACUUM_SIZE,
368 ARG_VACUUM_FILES,
369 ARG_VACUUM_TIME,
370 ARG_OUTPUT_FIELDS,
371 ARG_NAMESPACE,
372 ARG_LIST_NAMESPACES,
373 ARG_SYNCHRONIZE_ON_EXIT,
374 };
375
376 static const struct option options[] = {
377 { "help", no_argument, NULL, 'h' },
378 { "version" , no_argument, NULL, ARG_VERSION },
379 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
380 { "pager-end", no_argument, NULL, 'e' },
381 { "follow", no_argument, NULL, 'f' },
382 { "force", no_argument, NULL, ARG_FORCE },
383 { "output", required_argument, NULL, 'o' },
384 { "all", no_argument, NULL, 'a' },
385 { "full", no_argument, NULL, 'l' },
386 { "no-full", no_argument, NULL, ARG_NO_FULL },
387 { "lines", optional_argument, NULL, 'n' },
388 { "truncate-newline", no_argument, NULL, ARG_TRUNCATE_NEWLINE },
389 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
390 { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */
391 { "quiet", no_argument, NULL, 'q' },
392 { "merge", no_argument, NULL, 'm' },
393 { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */
394 { "boot", optional_argument, NULL, 'b' },
395 { "list-boots", no_argument, NULL, ARG_LIST_BOOTS },
396 { "list-invocations", no_argument, NULL, ARG_LIST_INVOCATIONS },
397 { "dmesg", no_argument, NULL, 'k' },
398 { "system", no_argument, NULL, ARG_SYSTEM },
399 { "user", no_argument, NULL, ARG_USER },
400 { "directory", required_argument, NULL, 'D' },
401 { "file", required_argument, NULL, 'i' },
402 { "root", required_argument, NULL, ARG_ROOT },
403 { "image", required_argument, NULL, ARG_IMAGE },
404 { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
405 { "header", no_argument, NULL, ARG_HEADER },
406 { "identifier", required_argument, NULL, 't' },
407 { "exclude-identifier", required_argument, NULL, 'T' },
408 { "priority", required_argument, NULL, 'p' },
409 { "facility", required_argument, NULL, ARG_FACILITY },
410 { "grep", required_argument, NULL, 'g' },
411 { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
412 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
413 { "interval", required_argument, NULL, ARG_INTERVAL },
414 { "verify", no_argument, NULL, ARG_VERIFY },
415 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
416 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
417 { "cursor", required_argument, NULL, 'c' },
418 { "cursor-file", required_argument, NULL, ARG_CURSOR_FILE },
419 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
420 { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
421 { "since", required_argument, NULL, 'S' },
422 { "until", required_argument, NULL, 'U' },
423 { "unit", required_argument, NULL, 'u' },
424 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
425 { "invocation", required_argument, NULL, ARG_INVOCATION },
426 { "field", required_argument, NULL, 'F' },
427 { "fields", no_argument, NULL, 'N' },
428 { "catalog", no_argument, NULL, 'x' },
429 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
430 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
431 { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
432 { "reverse", no_argument, NULL, 'r' },
433 { "machine", required_argument, NULL, 'M' },
434 { "utc", no_argument, NULL, ARG_UTC },
435 { "flush", no_argument, NULL, ARG_FLUSH },
436 { "relinquish-var", no_argument, NULL, ARG_RELINQUISH_VAR },
437 { "smart-relinquish-var", no_argument, NULL, ARG_SMART_RELINQUISH_VAR },
438 { "sync", no_argument, NULL, ARG_SYNC },
439 { "rotate", no_argument, NULL, ARG_ROTATE },
440 { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
441 { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
442 { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
443 { "no-hostname", no_argument, NULL, 'W' },
444 { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
445 { "namespace", required_argument, NULL, ARG_NAMESPACE },
446 { "list-namespaces", no_argument, NULL, ARG_LIST_NAMESPACES },
447 { "synchronize-on-exit", required_argument, NULL, ARG_SYNCHRONIZE_ON_EXIT },
448 {}
449 };
450
451 int c, r;
452
453 assert(argc >= 0);
454 assert(argv);
455
456 while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:T:u:INF:xrM:i:W", options, NULL)) >= 0)
457
458 switch (c) {
459
460 case 'h':
461 return help();
462
463 case ARG_VERSION:
464 return version();
465
466 case ARG_NO_PAGER:
467 arg_pager_flags |= PAGER_DISABLE;
468 break;
469
470 case 'e':
471 arg_pager_flags |= PAGER_JUMP_TO_END;
472 break;
473
474 case 'f':
475 arg_follow = true;
476 break;
477
478 case 'o':
479 if (streq(optarg, "help"))
480 return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
481
482 arg_output = output_mode_from_string(optarg);
483 if (arg_output < 0)
484 return log_error_errno(arg_output, "Unknown output format '%s'.", optarg);
485
486 if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
487 arg_quiet = true;
488
489 if (OUTPUT_MODE_IS_JSON(arg_output))
490 arg_json_format_flags = output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO;
491 else
492 arg_json_format_flags = SD_JSON_FORMAT_OFF;
493
494 break;
495
496 case 'l':
497 arg_full = true;
498 break;
499
500 case ARG_NO_FULL:
501 arg_full = false;
502 break;
503
504 case 'a':
505 arg_all = true;
506 break;
507
508 case 'n':
509 r = parse_lines(optarg ?: argv[optind], !optarg);
510 if (r < 0)
511 return r;
512 if (r > 0 && !optarg)
513 optind++;
514
515 break;
516
517 case ARG_NO_TAIL:
518 arg_no_tail = true;
519 break;
520
521 case ARG_TRUNCATE_NEWLINE:
522 arg_truncate_newline = true;
523 break;
524
525 case ARG_NEW_ID128:
526 arg_action = ACTION_NEW_ID128;
527 break;
528
529 case 'q':
530 arg_quiet = true;
531 break;
532
533 case 'm':
534 arg_merge = true;
535 break;
536
537 case ARG_THIS_BOOT:
538 arg_boot = true;
539 arg_boot_id = SD_ID128_NULL;
540 arg_boot_offset = 0;
541 break;
542
543 case 'b':
544 arg_boot = true;
545 arg_boot_id = SD_ID128_NULL;
546 arg_boot_offset = 0;
547
548 if (optarg) {
549 r = parse_id_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
550 if (r < 0)
551 return log_error_errno(r, "Failed to parse boot descriptor '%s'", optarg);
552
553 arg_boot = r;
554
555 } else if (optind < argc) {
556 /* Hmm, no argument? Maybe the next word on the command line is supposed to be the
557 * argument? Let's see if there is one and is parsable as a boot descriptor... */
558 r = parse_id_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset);
559 if (r >= 0) {
560 arg_boot = r;
561 optind++;
562 }
563 }
564 break;
565
566 case ARG_LIST_BOOTS:
567 arg_action = ACTION_LIST_BOOTS;
568 break;
569
570 case ARG_LIST_INVOCATIONS:
571 arg_action = ACTION_LIST_INVOCATIONS;
572 break;
573
574 case 'k':
575 arg_dmesg = true;
576 break;
577
578 case ARG_SYSTEM:
579 arg_journal_type |= SD_JOURNAL_SYSTEM;
580 break;
581
582 case ARG_USER:
583 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
584 break;
585
586 case 'M':
587 r = free_and_strdup_warn(&arg_machine, optarg);
588 if (r < 0)
589 return r;
590 break;
591
592 case ARG_NAMESPACE:
593 if (streq(optarg, "*")) {
594 arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
595 arg_namespace = mfree(arg_namespace);
596 } else if (startswith(optarg, "+")) {
597 arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
598 r = free_and_strdup_warn(&arg_namespace, optarg + 1);
599 if (r < 0)
600 return r;
601 } else if (isempty(optarg)) {
602 arg_namespace_flags = 0;
603 arg_namespace = mfree(arg_namespace);
604 } else {
605 arg_namespace_flags = 0;
606 r = free_and_strdup_warn(&arg_namespace, optarg);
607 if (r < 0)
608 return r;
609 }
610 break;
611
612 case ARG_LIST_NAMESPACES:
613 arg_action = ACTION_LIST_NAMESPACES;
614 break;
615
616 case 'D':
617 r = free_and_strdup_warn(&arg_directory, optarg);
618 if (r < 0)
619 return r;
620 break;
621
622 case 'i':
623 if (streq(optarg, "-"))
624 /* An undocumented feature: we can read journal files from STDIN. We don't document
625 * this though, since after all we only support this for mmap-able, seekable files, and
626 * not for example pipes which are probably the primary use case for reading things from
627 * STDIN. To avoid confusion we hence don't document this feature. */
628 arg_file_stdin = true;
629 else {
630 r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
631 if (r < 0)
632 return log_error_errno(r, "Failed to add paths: %m");
633 }
634 break;
635
636 case ARG_ROOT:
637 r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
638 if (r < 0)
639 return r;
640 break;
641
642 case ARG_IMAGE:
643 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
644 if (r < 0)
645 return r;
646 break;
647
648 case ARG_IMAGE_POLICY:
649 r = parse_image_policy_argument(optarg, &arg_image_policy);
650 if (r < 0)
651 return r;
652 break;
653
654 case 'c':
655 r = free_and_strdup_warn(&arg_cursor, optarg);
656 if (r < 0)
657 return r;
658 break;
659
660 case ARG_CURSOR_FILE:
661 r = free_and_strdup_warn(&arg_cursor_file, optarg);
662 if (r < 0)
663 return r;
664 break;
665
666 case ARG_AFTER_CURSOR:
667 r = free_and_strdup_warn(&arg_after_cursor, optarg);
668 if (r < 0)
669 return r;
670 break;
671
672 case ARG_SHOW_CURSOR:
673 arg_show_cursor = true;
674 break;
675
676 case ARG_HEADER:
677 arg_action = ACTION_PRINT_HEADER;
678 break;
679
680 case ARG_VERIFY:
681 arg_action = ACTION_VERIFY;
682 break;
683
684 case ARG_DISK_USAGE:
685 arg_action = ACTION_DISK_USAGE;
686 break;
687
688 case ARG_VACUUM_SIZE:
689 r = parse_size(optarg, 1024, &arg_vacuum_size);
690 if (r < 0)
691 return log_error_errno(r, "Failed to parse vacuum size: %s", optarg);
692
693 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
694 break;
695
696 case ARG_VACUUM_FILES:
697 r = safe_atou64(optarg, &arg_vacuum_n_files);
698 if (r < 0)
699 return log_error_errno(r, "Failed to parse vacuum files: %s", optarg);
700
701 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
702 break;
703
704 case ARG_VACUUM_TIME:
705 r = parse_sec(optarg, &arg_vacuum_time);
706 if (r < 0)
707 return log_error_errno(r, "Failed to parse vacuum time: %s", optarg);
708
709 arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
710 break;
711
712 #if HAVE_GCRYPT
713 case ARG_FORCE:
714 arg_force = true;
715 break;
716
717 case ARG_SETUP_KEYS:
718 arg_action = ACTION_SETUP_KEYS;
719 break;
720
721 case ARG_VERIFY_KEY:
722 erase_and_free(arg_verify_key);
723 arg_verify_key = strdup(optarg);
724 if (!arg_verify_key)
725 return log_oom();
726
727 /* Use memset not explicit_bzero() or similar so this doesn't look confusing
728 * in ps or htop output. */
729 memset(optarg, 'x', strlen(optarg));
730
731 arg_action = ACTION_VERIFY;
732 arg_merge = false;
733 break;
734
735 case ARG_INTERVAL:
736 r = parse_sec(optarg, &arg_interval);
737 if (r < 0 || arg_interval <= 0)
738 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
739 "Failed to parse sealing key change interval: %s", optarg);
740 break;
741 #else
742 case ARG_SETUP_KEYS:
743 case ARG_VERIFY_KEY:
744 case ARG_INTERVAL:
745 case ARG_FORCE:
746 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
747 "Compiled without forward-secure sealing support.");
748 #endif
749
750 case 'p': {
751 const char *dots;
752
753 dots = strstr(optarg, "..");
754 if (dots) {
755 _cleanup_free_ char *a = NULL;
756 int from, to, i;
757
758 /* a range */
759 a = strndup(optarg, dots - optarg);
760 if (!a)
761 return log_oom();
762
763 from = log_level_from_string(a);
764 to = log_level_from_string(dots + 2);
765
766 if (from < 0 || to < 0)
767 return log_error_errno(from < 0 ? from : to,
768 "Failed to parse log level range %s", optarg);
769
770 arg_priorities = 0;
771
772 if (from < to) {
773 for (i = from; i <= to; i++)
774 arg_priorities |= 1 << i;
775 } else {
776 for (i = to; i <= from; i++)
777 arg_priorities |= 1 << i;
778 }
779
780 } else {
781 int p, i;
782
783 p = log_level_from_string(optarg);
784 if (p < 0)
785 return log_error_errno(p, "Unknown log level %s", optarg);
786
787 arg_priorities = 0;
788
789 for (i = 0; i <= p; i++)
790 arg_priorities |= 1 << i;
791 }
792
793 break;
794 }
795
796 case ARG_FACILITY: {
797 const char *p;
798
799 for (p = optarg;;) {
800 _cleanup_free_ char *fac = NULL;
801 int num;
802
803 r = extract_first_word(&p, &fac, ",", 0);
804 if (r < 0)
805 return log_error_errno(r, "Failed to parse facilities: %s", optarg);
806 if (r == 0)
807 break;
808
809 if (streq(fac, "help")) {
810 help_facilities();
811 return 0;
812 }
813
814 num = log_facility_unshifted_from_string(fac);
815 if (num < 0)
816 return log_error_errno(num, "Bad --facility= argument \"%s\".", fac);
817
818 if (set_ensure_put(&arg_facilities, NULL, INT_TO_PTR(num)) < 0)
819 return log_oom();
820 }
821
822 break;
823 }
824
825 case 'g':
826 r = free_and_strdup_warn(&arg_pattern, optarg);
827 if (r < 0)
828 return r;
829 break;
830
831 case ARG_CASE_SENSITIVE:
832 if (optarg) {
833 r = parse_boolean(optarg);
834 if (r < 0)
835 return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
836 arg_case = r ? PATTERN_COMPILE_CASE_SENSITIVE : PATTERN_COMPILE_CASE_INSENSITIVE;
837 } else
838 arg_case = PATTERN_COMPILE_CASE_SENSITIVE;
839
840 break;
841
842 case 'S':
843 r = parse_timestamp(optarg, &arg_since);
844 if (r < 0)
845 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
846 "Failed to parse timestamp: %s", optarg);
847 arg_since_set = true;
848 break;
849
850 case 'U':
851 r = parse_timestamp(optarg, &arg_until);
852 if (r < 0)
853 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
854 "Failed to parse timestamp: %s", optarg);
855 arg_until_set = true;
856 break;
857
858 case 't':
859 r = strv_extend(&arg_syslog_identifier, optarg);
860 if (r < 0)
861 return log_oom();
862 break;
863
864 case 'T':
865 r = strv_extend(&arg_exclude_identifier, optarg);
866 if (r < 0)
867 return log_oom();
868 break;
869
870 case 'u':
871 r = strv_extend(&arg_system_units, optarg);
872 if (r < 0)
873 return log_oom();
874 break;
875
876 case ARG_USER_UNIT:
877 r = strv_extend(&arg_user_units, optarg);
878 if (r < 0)
879 return log_oom();
880 break;
881
882 case ARG_INVOCATION:
883 r = parse_id_descriptor(optarg, &arg_invocation_id, &arg_invocation_offset);
884 if (r < 0)
885 return log_error_errno(r, "Failed to parse invocation descriptor: %s", optarg);
886 arg_invocation = r;
887 break;
888
889 case 'I':
890 /* Equivalent to --invocation=0 */
891 arg_invocation = true;
892 arg_invocation_id = SD_ID128_NULL;
893 arg_invocation_offset = 0;
894 break;
895
896 case 'F':
897 arg_action = ACTION_LIST_FIELDS;
898 r = free_and_strdup_warn(&arg_field, optarg);
899 if (r < 0)
900 return r;
901 break;
902
903 case 'N':
904 arg_action = ACTION_LIST_FIELD_NAMES;
905 break;
906
907 case 'W':
908 arg_no_hostname = true;
909 break;
910
911 case 'x':
912 arg_catalog = true;
913 break;
914
915 case ARG_LIST_CATALOG:
916 arg_action = ACTION_LIST_CATALOG;
917 break;
918
919 case ARG_DUMP_CATALOG:
920 arg_action = ACTION_DUMP_CATALOG;
921 break;
922
923 case ARG_UPDATE_CATALOG:
924 arg_action = ACTION_UPDATE_CATALOG;
925 break;
926
927 case 'r':
928 arg_reverse = true;
929 break;
930
931 case ARG_UTC:
932 arg_utc = true;
933 break;
934
935 case ARG_FLUSH:
936 arg_action = ACTION_FLUSH;
937 break;
938
939 case ARG_SMART_RELINQUISH_VAR: {
940 int root_mnt_id, log_mnt_id;
941
942 /* Try to be smart about relinquishing access to /var/log/journal/ during shutdown:
943 * if it's on the same mount as the root file system there's no point in
944 * relinquishing access and we can leave journald write to it until the very last
945 * moment. */
946
947 r = path_get_mnt_id("/", &root_mnt_id);
948 if (r < 0)
949 log_debug_errno(r, "Failed to get root mount ID, ignoring: %m");
950 else {
951 r = path_get_mnt_id("/var/log/journal/", &log_mnt_id);
952 if (r < 0)
953 log_debug_errno(r, "Failed to get journal directory mount ID, ignoring: %m");
954 else if (root_mnt_id == log_mnt_id) {
955 log_debug("/var/log/journal/ is on root file system, not relinquishing access to /var.");
956 return 0;
957 } else
958 log_debug("/var/log/journal/ is not on the root file system, relinquishing access to it.");
959 }
960
961 _fallthrough_;
962 }
963
964 case ARG_RELINQUISH_VAR:
965 arg_action = ACTION_RELINQUISH_VAR;
966 break;
967
968 case ARG_ROTATE:
969 arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
970 break;
971
972 case ARG_SYNC:
973 arg_action = ACTION_SYNC;
974 break;
975
976 case ARG_OUTPUT_FIELDS: {
977 _cleanup_strv_free_ char **v = NULL;
978
979 v = strv_split(optarg, ",");
980 if (!v)
981 return log_oom();
982
983 r = set_put_strdupv(&arg_output_fields, v);
984 if (r < 0)
985 return log_oom();
986
987 break;
988 }
989
990 case ARG_SYNCHRONIZE_ON_EXIT:
991 r = parse_boolean_argument("--synchronize-on-exit", optarg, &arg_synchronize_on_exit);
992 if (r < 0)
993 return r;
994
995 break;
996
997 case '?':
998 return -EINVAL;
999
1000 default:
1001 assert_not_reached();
1002 }
1003
1004 if (arg_no_tail)
1005 arg_lines = ARG_LINES_ALL;
1006
1007 if (arg_lines == ARG_LINES_DEFAULT) {
1008 if (arg_follow && !arg_since_set)
1009 arg_lines = 10;
1010 else if (FLAGS_SET(arg_pager_flags, PAGER_JUMP_TO_END))
1011 arg_lines = 1000;
1012 }
1013
1014 if (arg_boot < 0)
1015 /* Show the current boot if -f/--follow, -k/--dmesg, or -e/--pager-end is specified unless
1016 * -m/--merge is specified. */
1017 arg_boot = !arg_merge && (arg_follow || arg_dmesg || FLAGS_SET(arg_pager_flags, PAGER_JUMP_TO_END));
1018 if (!arg_boot) {
1019 /* Clear the boot ID and offset if -b/--boot is unspecified for safety. */
1020 arg_boot_id = SD_ID128_NULL;
1021 arg_boot_offset = 0;
1022 }
1023
1024 if (!!arg_directory + !!arg_file + arg_file_stdin + !!arg_machine + !!arg_root + !!arg_image > 1)
1025 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1026 "Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root=, --image=.");
1027
1028 if (arg_since_set && arg_until_set && arg_since > arg_until)
1029 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1030 "--since= must be before --until=.");
1031
1032 if (!!arg_cursor + !!arg_after_cursor + !!arg_cursor_file + !!arg_since_set > 1)
1033 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1034 "Please specify only one of --since=, --cursor=, --cursor-file=, and --after-cursor=.");
1035
1036 if (arg_follow && arg_reverse)
1037 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1038 "Please specify either --reverse or --follow, not both.");
1039
1040 if (arg_action == ACTION_SHOW && arg_lines >= 0 && arg_lines_oldest && (arg_reverse || arg_follow))
1041 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1042 "--lines=+N is unsupported when --reverse or --follow is specified.");
1043
1044 if (!IN_SET(arg_action, ACTION_SHOW, ACTION_DUMP_CATALOG, ACTION_LIST_CATALOG) && optind < argc)
1045 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1046 "Extraneous arguments starting with '%s'",
1047 argv[optind]);
1048
1049 if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && arg_merge)
1050 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1051 "Using --boot or --list-boots with --merge is not supported.");
1052
1053 if (!strv_isempty(arg_system_units) && arg_journal_type == SD_JOURNAL_CURRENT_USER) {
1054 /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user
1055 * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice
1056 * to users, and automatically turn --unit= into --user-unit= if combined with --user. */
1057 r = strv_extend_strv(&arg_user_units, arg_system_units, true);
1058 if (r < 0)
1059 return r;
1060
1061 arg_system_units = strv_free(arg_system_units);
1062 }
1063
1064 if (arg_pattern) {
1065 r = pattern_compile_and_log(arg_pattern, arg_case, &arg_compiled_pattern);
1066 if (r < 0)
1067 return r;
1068
1069 /* When --grep is used along with --lines without '+', i.e. when we start from the end of the
1070 * journal, we don't know how many lines we can print. So we search backwards and count until
1071 * enough lines have been printed or we hit the head.
1072 * An exception is that --follow might set arg_lines, so let's not imply --reverse
1073 * if that is specified. */
1074 if (arg_lines_needs_seek_end() && !arg_follow)
1075 arg_reverse = true;
1076 }
1077
1078 if (!arg_follow)
1079 arg_journal_additional_open_flags = SD_JOURNAL_ASSUME_IMMUTABLE;
1080
1081 return 1;
1082 }
1083
1084 static int run(int argc, char *argv[]) {
1085 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
1086 _cleanup_(umount_and_freep) char *mounted_dir = NULL;
1087 _cleanup_strv_free_ char **args = NULL;
1088 int r;
1089
1090 setlocale(LC_ALL, "");
1091 log_setup();
1092
1093 r = parse_argv(argc, argv);
1094 if (r <= 0)
1095 return r;
1096
1097 r = strv_copy_unless_empty(strv_skip(argv, optind), &args);
1098 if (r < 0)
1099 return log_oom();
1100
1101 if (arg_image) {
1102 assert(!arg_root);
1103
1104 r = mount_image_privately_interactively(
1105 arg_image,
1106 arg_image_policy,
1107 DISSECT_IMAGE_GENERIC_ROOT |
1108 DISSECT_IMAGE_REQUIRE_ROOT |
1109 DISSECT_IMAGE_VALIDATE_OS |
1110 DISSECT_IMAGE_RELAX_VAR_CHECK |
1111 (arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY) |
1112 DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
1113 &mounted_dir,
1114 /* ret_dir_fd= */ NULL,
1115 &loop_device);
1116 if (r < 0)
1117 return r;
1118
1119 arg_root = strdup(mounted_dir);
1120 if (!arg_root)
1121 return log_oom();
1122 }
1123
1124 switch (arg_action) {
1125
1126 case ACTION_SHOW:
1127 return action_show(args);
1128
1129 case ACTION_NEW_ID128:
1130 return id128_print_new(ID128_PRINT_PRETTY);
1131
1132 case ACTION_SETUP_KEYS:
1133 return action_setup_keys();
1134
1135 case ACTION_LIST_CATALOG:
1136 case ACTION_DUMP_CATALOG:
1137 return action_list_catalog(args);
1138
1139 case ACTION_UPDATE_CATALOG:
1140 return action_update_catalog();
1141
1142 case ACTION_PRINT_HEADER:
1143 return action_print_header();
1144
1145 case ACTION_VERIFY:
1146 return action_verify();
1147
1148 case ACTION_DISK_USAGE:
1149 return action_disk_usage();
1150
1151 case ACTION_LIST_BOOTS:
1152 return action_list_boots();
1153
1154 case ACTION_LIST_FIELDS:
1155 return action_list_fields();
1156
1157 case ACTION_LIST_FIELD_NAMES:
1158 return action_list_field_names();
1159
1160 case ACTION_LIST_INVOCATIONS:
1161 return action_list_invocations();
1162
1163 case ACTION_LIST_NAMESPACES:
1164 return action_list_namespaces();
1165
1166 case ACTION_FLUSH:
1167 return action_flush_to_var();
1168
1169 case ACTION_RELINQUISH_VAR:
1170 return action_relinquish_var();
1171
1172 case ACTION_SYNC:
1173 return action_sync();
1174
1175 case ACTION_ROTATE:
1176 return action_rotate();
1177
1178 case ACTION_VACUUM:
1179 return action_vacuum();
1180
1181 case ACTION_ROTATE_AND_VACUUM:
1182 return action_rotate_and_vacuum();
1183
1184 default:
1185 assert_not_reached();
1186 }
1187 }
1188
1189 DEFINE_MAIN_FUNCTION(run);