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