]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journal: implement message catalog
[thirdparty/systemd.git] / src / journal / journalctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <locale.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sys/poll.h>
31 #include <time.h>
32 #include <getopt.h>
33 #include <signal.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 #include <linux/fs.h>
37
38 #include <systemd/sd-journal.h>
39
40 #include "log.h"
41 #include "util.h"
42 #include "path-util.h"
43 #include "build.h"
44 #include "pager.h"
45 #include "logs-show.h"
46 #include "strv.h"
47 #include "journal-internal.h"
48 #include "journal-def.h"
49 #include "journal-verify.h"
50 #include "journal-authenticate.h"
51 #include "journal-qrcode.h"
52 #include "fsprg.h"
53 #include "unit-name.h"
54 #include "catalog.h"
55
56 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
57
58 static OutputMode arg_output = OUTPUT_SHORT;
59 static bool arg_follow = false;
60 static bool arg_all = false;
61 static bool arg_no_pager = false;
62 static unsigned arg_lines = 0;
63 static bool arg_no_tail = false;
64 static bool arg_quiet = false;
65 static bool arg_merge = false;
66 static bool arg_this_boot = false;
67 static const char *arg_cursor = NULL;
68 static const char *arg_directory = NULL;
69 static int arg_priorities = 0xFF;
70 static const char *arg_verify_key = NULL;
71 #ifdef HAVE_GCRYPT
72 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
73 #endif
74 static usec_t arg_since, arg_until;
75 static bool arg_since_set = false, arg_until_set = false;
76 static const char *arg_unit = NULL;
77 static const char *arg_field = NULL;
78 static bool arg_catalog = false;
79
80 static enum {
81 ACTION_SHOW,
82 ACTION_NEW_ID128,
83 ACTION_PRINT_HEADER,
84 ACTION_SETUP_KEYS,
85 ACTION_VERIFY,
86 ACTION_DISK_USAGE,
87 ACTION_LIST_CATALOG,
88 ACTION_UPDATE_CATALOG
89 } arg_action = ACTION_SHOW;
90
91 static int help(void) {
92
93 printf("%s [OPTIONS...] [MATCHES...]\n\n"
94 "Query the journal.\n\n"
95 "Flags:\n"
96 " --since=DATE Start showing entries newer or of the specified date\n"
97 " --until=DATE Stop showing entries older or of the specified date\n"
98 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
99 " -b --this-boot Show data only from current boot\n"
100 " -u --unit=UNIT Show data only from the specified unit\n"
101 " -p --priority=RANGE Show only messages within the specified priority range\n"
102 " -f --follow Follow journal\n"
103 " -n --lines[=INTEGER] Number of journal entries to show\n"
104 " --no-tail Show all lines, even in follow mode\n"
105 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
106 " verbose, export, json, json-pretty, json-sse, cat)\n"
107 " -x --catalog Add message explanations where available\n"
108 " -a --all Show all fields, including long and unprintable\n"
109 " -q --quiet Don't show privilege warning\n"
110 " --no-pager Do not pipe output into a pager\n"
111 " -m --merge Show entries from all available journals\n"
112 " -D --directory=PATH Show journal files from directory\n"
113 #ifdef HAVE_GCRYPT
114 " --interval=TIME Time interval for changing the FSS sealing key\n"
115 " --verify-key=KEY Specify FSS verification key\n"
116 #endif
117 "\nCommands:\n"
118 " -h --help Show this help\n"
119 " --version Show package version\n"
120 " --new-id128 Generate a new 128 Bit ID\n"
121 " --header Show journal header information\n"
122 " --disk-usage Show total disk usage\n"
123 " -F --field=FIELD List all values a certain field takes\n"
124 " --list-catalog Show message IDs of all entries in the message catalog\n"
125 " --update-catalog Update the message catalog database\n"
126 #ifdef HAVE_GCRYPT
127 " --setup-keys Generate new FSS key pair\n"
128 " --verify Verify journal file consistency\n"
129 #endif
130 , program_invocation_short_name);
131
132 return 0;
133 }
134
135 static int parse_argv(int argc, char *argv[]) {
136
137 enum {
138 ARG_VERSION = 0x100,
139 ARG_NO_PAGER,
140 ARG_NO_TAIL,
141 ARG_NEW_ID128,
142 ARG_HEADER,
143 ARG_SETUP_KEYS,
144 ARG_INTERVAL,
145 ARG_VERIFY,
146 ARG_VERIFY_KEY,
147 ARG_DISK_USAGE,
148 ARG_SINCE,
149 ARG_UNTIL,
150 ARG_LIST_CATALOG,
151 ARG_UPDATE_CATALOG
152 };
153
154 static const struct option options[] = {
155 { "help", no_argument, NULL, 'h' },
156 { "version" , no_argument, NULL, ARG_VERSION },
157 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
158 { "follow", no_argument, NULL, 'f' },
159 { "output", required_argument, NULL, 'o' },
160 { "all", no_argument, NULL, 'a' },
161 { "lines", optional_argument, NULL, 'n' },
162 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
163 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
164 { "quiet", no_argument, NULL, 'q' },
165 { "merge", no_argument, NULL, 'm' },
166 { "this-boot", no_argument, NULL, 'b' },
167 { "directory", required_argument, NULL, 'D' },
168 { "header", no_argument, NULL, ARG_HEADER },
169 { "priority", required_argument, NULL, 'p' },
170 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
171 { "interval", required_argument, NULL, ARG_INTERVAL },
172 { "verify", no_argument, NULL, ARG_VERIFY },
173 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
174 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
175 { "cursor", required_argument, NULL, 'c' },
176 { "since", required_argument, NULL, ARG_SINCE },
177 { "until", required_argument, NULL, ARG_UNTIL },
178 { "unit", required_argument, NULL, 'u' },
179 { "field", required_argument, NULL, 'F' },
180 { "catalog", no_argument, NULL, 'x' },
181 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
182 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
183 { NULL, 0, NULL, 0 }
184 };
185
186 int c, r;
187
188 assert(argc >= 0);
189 assert(argv);
190
191 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) {
192
193 switch (c) {
194
195 case 'h':
196 help();
197 return 0;
198
199 case ARG_VERSION:
200 puts(PACKAGE_STRING);
201 puts(DISTRIBUTION);
202 puts(SYSTEMD_FEATURES);
203 return 0;
204
205 case ARG_NO_PAGER:
206 arg_no_pager = true;
207 break;
208
209 case 'f':
210 arg_follow = true;
211 break;
212
213 case 'o':
214 arg_output = output_mode_from_string(optarg);
215 if (arg_output < 0) {
216 log_error("Unknown output format '%s'.", optarg);
217 return -EINVAL;
218 }
219
220 if (arg_output == OUTPUT_EXPORT ||
221 arg_output == OUTPUT_JSON ||
222 arg_output == OUTPUT_JSON_PRETTY ||
223 arg_output == OUTPUT_JSON_SSE ||
224 arg_output == OUTPUT_CAT)
225 arg_quiet = true;
226
227 break;
228
229 case 'a':
230 arg_all = true;
231 break;
232
233 case 'n':
234 if (optarg) {
235 r = safe_atou(optarg, &arg_lines);
236 if (r < 0 || arg_lines <= 0) {
237 log_error("Failed to parse lines '%s'", optarg);
238 return -EINVAL;
239 }
240 } else
241 arg_lines = 10;
242
243 break;
244
245 case ARG_NO_TAIL:
246 arg_no_tail = true;
247 break;
248
249 case ARG_NEW_ID128:
250 arg_action = ACTION_NEW_ID128;
251 break;
252
253 case 'q':
254 arg_quiet = true;
255 break;
256
257 case 'm':
258 arg_merge = true;
259 break;
260
261 case 'b':
262 arg_this_boot = true;
263 break;
264
265 case 'D':
266 arg_directory = optarg;
267 break;
268
269 case 'c':
270 arg_cursor = optarg;
271 break;
272
273 case ARG_HEADER:
274 arg_action = ACTION_PRINT_HEADER;
275 break;
276
277 case ARG_VERIFY:
278 arg_action = ACTION_VERIFY;
279 break;
280
281 case ARG_DISK_USAGE:
282 arg_action = ACTION_DISK_USAGE;
283 break;
284
285 #ifdef HAVE_GCRYPT
286 case ARG_SETUP_KEYS:
287 arg_action = ACTION_SETUP_KEYS;
288 break;
289
290
291 case ARG_VERIFY_KEY:
292 arg_action = ACTION_VERIFY;
293 arg_verify_key = optarg;
294 arg_merge = false;
295 break;
296
297 case ARG_INTERVAL:
298 r = parse_usec(optarg, &arg_interval);
299 if (r < 0 || arg_interval <= 0) {
300 log_error("Failed to parse sealing key change interval: %s", optarg);
301 return -EINVAL;
302 }
303 break;
304 #else
305 case ARG_SETUP_KEYS:
306 case ARG_VERIFY_KEY:
307 case ARG_INTERVAL:
308 log_error("Forward-secure sealing not available.");
309 return -ENOTSUP;
310 #endif
311
312 case 'p': {
313 const char *dots;
314
315 dots = strstr(optarg, "..");
316 if (dots) {
317 char *a;
318 int from, to, i;
319
320 /* a range */
321 a = strndup(optarg, dots - optarg);
322 if (!a)
323 return log_oom();
324
325 from = log_level_from_string(a);
326 to = log_level_from_string(dots + 2);
327 free(a);
328
329 if (from < 0 || to < 0) {
330 log_error("Failed to parse log level range %s", optarg);
331 return -EINVAL;
332 }
333
334 arg_priorities = 0;
335
336 if (from < to) {
337 for (i = from; i <= to; i++)
338 arg_priorities |= 1 << i;
339 } else {
340 for (i = to; i <= from; i++)
341 arg_priorities |= 1 << i;
342 }
343
344 } else {
345 int p, i;
346
347 p = log_level_from_string(optarg);
348 if (p < 0) {
349 log_error("Unknown log level %s", optarg);
350 return -EINVAL;
351 }
352
353 arg_priorities = 0;
354
355 for (i = 0; i <= p; i++)
356 arg_priorities |= 1 << i;
357 }
358
359 break;
360 }
361
362 case ARG_SINCE:
363 r = parse_timestamp(optarg, &arg_since);
364 if (r < 0) {
365 log_error("Failed to parse timestamp: %s", optarg);
366 return -EINVAL;
367 }
368 arg_since_set = true;
369 break;
370
371 case ARG_UNTIL:
372 r = parse_timestamp(optarg, &arg_until);
373 if (r < 0) {
374 log_error("Failed to parse timestamp: %s", optarg);
375 return -EINVAL;
376 }
377 arg_until_set = true;
378 break;
379
380 case 'u':
381 arg_unit = optarg;
382 break;
383
384 case '?':
385 return -EINVAL;
386
387 case 'F':
388 arg_field = optarg;
389 break;
390
391 case 'x':
392 arg_catalog = true;
393 break;
394
395 case ARG_LIST_CATALOG:
396 arg_action = ACTION_LIST_CATALOG;
397 break;
398
399 case ARG_UPDATE_CATALOG:
400 arg_action = ACTION_UPDATE_CATALOG;
401 break;
402
403 default:
404 log_error("Unknown option code %c", c);
405 return -EINVAL;
406 }
407 }
408
409 if (arg_follow && !arg_no_tail && arg_lines <= 0)
410 arg_lines = 10;
411
412 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
413 log_error("--since= must be before --until=.");
414 return -EINVAL;
415 }
416
417 if (arg_cursor && arg_since_set) {
418 log_error("Please specify either --since= or --cursor=, not both.");
419 return -EINVAL;
420 }
421
422 return 1;
423 }
424
425 static int generate_new_id128(void) {
426 sd_id128_t id;
427 int r;
428 unsigned i;
429
430 r = sd_id128_randomize(&id);
431 if (r < 0) {
432 log_error("Failed to generate ID: %s", strerror(-r));
433 return r;
434 }
435
436 printf("As string:\n"
437 SD_ID128_FORMAT_STR "\n\n"
438 "As UUID:\n"
439 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
440 "As macro:\n"
441 "#define MESSAGE_XYZ SD_ID128_MAKE(",
442 SD_ID128_FORMAT_VAL(id),
443 SD_ID128_FORMAT_VAL(id));
444
445 for (i = 0; i < 16; i++)
446 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
447
448 fputs(")\n", stdout);
449
450 return 0;
451 }
452
453 static int add_matches(sd_journal *j, char **args) {
454 char **i;
455 int r;
456
457 assert(j);
458
459 STRV_FOREACH(i, args) {
460
461 if (streq(*i, "+"))
462 r = sd_journal_add_disjunction(j);
463 else if (path_is_absolute(*i)) {
464 char *p, *t = NULL;
465 const char *path;
466 struct stat st;
467
468 p = canonicalize_file_name(*i);
469 path = p ? p : *i;
470
471 if (stat(path, &st) < 0) {
472 free(p);
473 log_error("Couldn't stat file: %m");
474 return -errno;
475 }
476
477 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
478 t = strappend("_EXE=", path);
479 else if (S_ISCHR(st.st_mode))
480 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
481 else if (S_ISBLK(st.st_mode))
482 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
483 else {
484 free(p);
485 log_error("File is not a device node, regular file or is not executable: %s", *i);
486 return -EINVAL;
487 }
488
489 free(p);
490
491 if (!t)
492 return log_oom();
493
494 r = sd_journal_add_match(j, t, 0);
495 free(t);
496 } else
497 r = sd_journal_add_match(j, *i, 0);
498
499 if (r < 0) {
500 log_error("Failed to add match '%s': %s", *i, strerror(-r));
501 return r;
502 }
503 }
504
505 return 0;
506 }
507
508 static int add_this_boot(sd_journal *j) {
509 char match[9+32+1] = "_BOOT_ID=";
510 sd_id128_t boot_id;
511 int r;
512
513 assert(j);
514
515 if (!arg_this_boot)
516 return 0;
517
518 r = sd_id128_get_boot(&boot_id);
519 if (r < 0) {
520 log_error("Failed to get boot id: %s", strerror(-r));
521 return r;
522 }
523
524 sd_id128_to_string(boot_id, match + 9);
525 r = sd_journal_add_match(j, match, strlen(match));
526 if (r < 0) {
527 log_error("Failed to add match: %s", strerror(-r));
528 return r;
529 }
530
531 return 0;
532 }
533
534 static int add_unit(sd_journal *j) {
535 _cleanup_free_ char *m = NULL, *u = NULL;
536 int r;
537
538 assert(j);
539
540 if (isempty(arg_unit))
541 return 0;
542
543 u = unit_name_mangle(arg_unit);
544 if (!u)
545 return log_oom();
546
547 m = strappend("_SYSTEMD_UNIT=", u);
548 if (!m)
549 return log_oom();
550
551 r = sd_journal_add_match(j, m, strlen(m));
552 if (r < 0) {
553 log_error("Failed to add match: %s", strerror(-r));
554 return r;
555 }
556
557 return 0;
558 }
559
560 static int add_priorities(sd_journal *j) {
561 char match[] = "PRIORITY=0";
562 int i, r;
563
564 assert(j);
565
566 if (arg_priorities == 0xFF)
567 return 0;
568
569 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
570 if (arg_priorities & (1 << i)) {
571 match[sizeof(match)-2] = '0' + i;
572
573 r = sd_journal_add_match(j, match, strlen(match));
574 if (r < 0) {
575 log_error("Failed to add match: %s", strerror(-r));
576 return r;
577 }
578 }
579
580 return 0;
581 }
582
583 static int setup_keys(void) {
584 #ifdef HAVE_GCRYPT
585 size_t mpk_size, seed_size, state_size, i;
586 uint8_t *mpk, *seed, *state;
587 ssize_t l;
588 int fd = -1, r, attr = 0;
589 sd_id128_t machine, boot;
590 char *p = NULL, *k = NULL;
591 struct FSSHeader h;
592 uint64_t n;
593
594 r = sd_id128_get_machine(&machine);
595 if (r < 0) {
596 log_error("Failed to get machine ID: %s", strerror(-r));
597 return r;
598 }
599
600 r = sd_id128_get_boot(&boot);
601 if (r < 0) {
602 log_error("Failed to get boot ID: %s", strerror(-r));
603 return r;
604 }
605
606 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
607 SD_ID128_FORMAT_VAL(machine)) < 0)
608 return log_oom();
609
610 if (access(p, F_OK) >= 0) {
611 log_error("Sealing key file %s exists already.", p);
612 r = -EEXIST;
613 goto finish;
614 }
615
616 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
617 SD_ID128_FORMAT_VAL(machine)) < 0) {
618 r = log_oom();
619 goto finish;
620 }
621
622 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
623 mpk = alloca(mpk_size);
624
625 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
626 seed = alloca(seed_size);
627
628 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
629 state = alloca(state_size);
630
631 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
632 if (fd < 0) {
633 log_error("Failed to open /dev/random: %m");
634 r = -errno;
635 goto finish;
636 }
637
638 log_info("Generating seed...");
639 l = loop_read(fd, seed, seed_size, true);
640 if (l < 0 || (size_t) l != seed_size) {
641 log_error("Failed to read random seed: %s", strerror(EIO));
642 r = -EIO;
643 goto finish;
644 }
645
646 log_info("Generating key pair...");
647 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
648
649 log_info("Generating sealing key...");
650 FSPRG_GenState0(state, mpk, seed, seed_size);
651
652 assert(arg_interval > 0);
653
654 n = now(CLOCK_REALTIME);
655 n /= arg_interval;
656
657 close_nointr_nofail(fd);
658 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
659 if (fd < 0) {
660 log_error("Failed to open %s: %m", k);
661 r = -errno;
662 goto finish;
663 }
664
665 /* Enable secure remove, exclusion from dump, synchronous
666 * writing and in-place updating */
667 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
668 log_warning("FS_IOC_GETFLAGS failed: %m");
669
670 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
671
672 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
673 log_warning("FS_IOC_SETFLAGS failed: %m");
674
675 zero(h);
676 memcpy(h.signature, "KSHHRHLP", 8);
677 h.machine_id = machine;
678 h.boot_id = boot;
679 h.header_size = htole64(sizeof(h));
680 h.start_usec = htole64(n * arg_interval);
681 h.interval_usec = htole64(arg_interval);
682 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
683 h.fsprg_state_size = htole64(state_size);
684
685 l = loop_write(fd, &h, sizeof(h), false);
686 if (l < 0 || (size_t) l != sizeof(h)) {
687 log_error("Failed to write header: %s", strerror(EIO));
688 r = -EIO;
689 goto finish;
690 }
691
692 l = loop_write(fd, state, state_size, false);
693 if (l < 0 || (size_t) l != state_size) {
694 log_error("Failed to write state: %s", strerror(EIO));
695 r = -EIO;
696 goto finish;
697 }
698
699 if (link(k, p) < 0) {
700 log_error("Failed to link file: %m");
701 r = -errno;
702 goto finish;
703 }
704
705 if (on_tty()) {
706 fprintf(stderr,
707 "\n"
708 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
709 "the following local file. This key file is automatically updated when the\n"
710 "sealing key is advanced. It should not be used on multiple hosts.\n"
711 "\n"
712 "\t%s\n"
713 "\n"
714 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
715 "at a safe location and should not be saved locally on disk.\n"
716 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
717 fflush(stderr);
718 }
719 for (i = 0; i < seed_size; i++) {
720 if (i > 0 && i % 3 == 0)
721 putchar('-');
722 printf("%02x", ((uint8_t*) seed)[i]);
723 }
724
725 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
726
727 if (on_tty()) {
728 char tsb[FORMAT_TIMESPAN_MAX], *hn;
729
730 fprintf(stderr,
731 ANSI_HIGHLIGHT_OFF "\n"
732 "The sealing key is automatically changed every %s.\n",
733 format_timespan(tsb, sizeof(tsb), arg_interval));
734
735 hn = gethostname_malloc();
736
737 if (hn) {
738 hostname_cleanup(hn);
739 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
740 } else
741 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
742
743 #ifdef HAVE_QRENCODE
744 /* If this is not an UTF-8 system don't print any QR codes */
745 if (is_locale_utf8()) {
746 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
747 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
748 }
749 #endif
750 free(hn);
751 }
752
753 r = 0;
754
755 finish:
756 if (fd >= 0)
757 close_nointr_nofail(fd);
758
759 if (k) {
760 unlink(k);
761 free(k);
762 }
763
764 free(p);
765
766 return r;
767 #else
768 log_error("Forward-secure sealing not available.");
769 return -ENOTSUP;
770 #endif
771 }
772
773 static int verify(sd_journal *j) {
774 int r = 0;
775 Iterator i;
776 JournalFile *f;
777
778 assert(j);
779
780 log_show_color(true);
781
782 HASHMAP_FOREACH(f, j->files, i) {
783 int k;
784 usec_t first, validated, last;
785
786 #ifdef HAVE_GCRYPT
787 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
788 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
789 #endif
790
791 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
792 if (k == -EINVAL) {
793 /* If the key was invalid give up right-away. */
794 return k;
795 } else if (k < 0) {
796 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
797 r = k;
798 } else {
799 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
800 log_info("PASS: %s", f->path);
801
802 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
803 if (validated > 0) {
804 log_info("=> Validated from %s to %s, final %s entries not sealed.",
805 format_timestamp(a, sizeof(a), first),
806 format_timestamp(b, sizeof(b), validated),
807 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
808 } else if (last > 0)
809 log_info("=> No sealing yet, %s of entries not sealed.",
810 format_timespan(c, sizeof(c), last - first));
811 else
812 log_info("=> No sealing yet, no entries in file.");
813 }
814 }
815 }
816
817 return r;
818 }
819
820 static int access_check(void) {
821
822 #ifdef HAVE_ACL
823 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
824 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
825 return -EACCES;
826 }
827
828 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
829 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
830 #else
831 if (geteuid() != 0 && in_group("adm") <= 0) {
832 log_error("No access to messages. Only users in the group 'adm' can see messages.");
833 return -EACCES;
834 }
835 #endif
836
837 return 0;
838 }
839
840 int main(int argc, char *argv[]) {
841 int r;
842 sd_journal *j = NULL;
843 bool need_seek = false;
844 sd_id128_t previous_boot_id;
845 bool previous_boot_id_valid = false;
846 unsigned n_shown = 0;
847
848 setlocale(LC_ALL, "");
849 log_parse_environment();
850 log_open();
851
852 r = parse_argv(argc, argv);
853 if (r <= 0)
854 goto finish;
855
856 signal(SIGWINCH, columns_lines_cache_reset);
857
858 if (arg_action == ACTION_NEW_ID128) {
859 r = generate_new_id128();
860 goto finish;
861 }
862
863 if (arg_action == ACTION_SETUP_KEYS) {
864 r = setup_keys();
865 goto finish;
866 }
867
868 if (arg_action == ACTION_LIST_CATALOG) {
869 r = catalog_list(stdout);
870 goto finish;
871 }
872
873 if (arg_action == ACTION_UPDATE_CATALOG) {
874 r = catalog_update();
875 goto finish;
876 }
877
878 r = access_check();
879 if (r < 0)
880 goto finish;
881
882 if (arg_directory)
883 r = sd_journal_open_directory(&j, arg_directory, 0);
884 else
885 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
886 if (r < 0) {
887 log_error("Failed to open journal: %s", strerror(-r));
888 goto finish;
889 }
890
891 if (arg_action == ACTION_VERIFY) {
892 r = verify(j);
893 goto finish;
894 }
895
896 if (arg_action == ACTION_PRINT_HEADER) {
897 journal_print_header(j);
898 r = 0;
899 goto finish;
900 }
901
902 if (arg_action == ACTION_DISK_USAGE) {
903 uint64_t bytes;
904 char sbytes[FORMAT_BYTES_MAX];
905
906 r = sd_journal_get_usage(j, &bytes);
907 if (r < 0)
908 goto finish;
909
910 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
911 r = 0;
912 goto finish;
913 }
914
915 r = add_this_boot(j);
916 if (r < 0)
917 goto finish;
918
919 r = add_unit(j);
920 if (r < 0)
921 goto finish;
922
923 r = add_matches(j, argv + optind);
924 if (r < 0)
925 goto finish;
926
927 r = add_priorities(j);
928 if (r < 0)
929 goto finish;
930
931 if (arg_field) {
932 const void *data;
933 size_t size;
934
935 r = sd_journal_query_unique(j, arg_field);
936 if (r < 0) {
937 log_error("Failed to query unique data objects: %s", strerror(-r));
938 goto finish;
939 }
940
941 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
942 const void *eq;
943
944 if (arg_lines > 0 && n_shown >= arg_lines)
945 break;
946
947 eq = memchr(data, '=', size);
948 if (eq)
949 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
950 else
951 printf("%.*s\n", (int) size, (const char*) data);
952
953 n_shown ++;
954 }
955
956 r = 0;
957 goto finish;
958 }
959
960 if (arg_cursor) {
961 r = sd_journal_seek_cursor(j, arg_cursor);
962 if (r < 0) {
963 log_error("Failed to seek to cursor: %s", strerror(-r));
964 goto finish;
965 }
966
967 r = sd_journal_next(j);
968
969 } else if (arg_since_set) {
970 r = sd_journal_seek_realtime_usec(j, arg_since);
971 if (r < 0) {
972 log_error("Failed to seek to date: %s", strerror(-r));
973 goto finish;
974 }
975 r = sd_journal_next(j);
976
977 } else if (arg_lines > 0) {
978 r = sd_journal_seek_tail(j);
979 if (r < 0) {
980 log_error("Failed to seek to tail: %s", strerror(-r));
981 goto finish;
982 }
983
984 r = sd_journal_previous_skip(j, arg_lines);
985
986 } else {
987 r = sd_journal_seek_head(j);
988 if (r < 0) {
989 log_error("Failed to seek to head: %s", strerror(-r));
990 goto finish;
991 }
992
993 r = sd_journal_next(j);
994 }
995
996 if (r < 0) {
997 log_error("Failed to iterate through journal: %s", strerror(-r));
998 goto finish;
999 }
1000
1001 if (!arg_no_pager && !arg_follow)
1002 pager_open();
1003
1004 if (!arg_quiet) {
1005 usec_t start, end;
1006 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1007
1008 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1009 if (r < 0) {
1010 log_error("Failed to get cutoff: %s", strerror(-r));
1011 goto finish;
1012 }
1013
1014 if (r > 0) {
1015 if (arg_follow)
1016 printf("-- Logs begin at %s. --\n",
1017 format_timestamp(start_buf, sizeof(start_buf), start));
1018 else
1019 printf("-- Logs begin at %s, end at %s. --\n",
1020 format_timestamp(start_buf, sizeof(start_buf), start),
1021 format_timestamp(end_buf, sizeof(end_buf), end));
1022 }
1023 }
1024
1025 for (;;) {
1026 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
1027 int flags;
1028
1029 if (need_seek) {
1030 r = sd_journal_next(j);
1031 if (r < 0) {
1032 log_error("Failed to iterate through journal: %s", strerror(-r));
1033 goto finish;
1034 }
1035 }
1036
1037 if (r == 0)
1038 break;
1039
1040 if (arg_until_set) {
1041 usec_t usec;
1042
1043 r = sd_journal_get_realtime_usec(j, &usec);
1044 if (r < 0) {
1045 log_error("Failed to determine timestamp: %s", strerror(-r));
1046 goto finish;
1047 }
1048 }
1049
1050 if (!arg_merge) {
1051 sd_id128_t boot_id;
1052
1053 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1054 if (r >= 0) {
1055 if (previous_boot_id_valid &&
1056 !sd_id128_equal(boot_id, previous_boot_id))
1057 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1058
1059 previous_boot_id = boot_id;
1060 previous_boot_id_valid = true;
1061 }
1062 }
1063
1064 flags =
1065 arg_all * OUTPUT_SHOW_ALL |
1066 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1067 on_tty() * OUTPUT_COLOR |
1068 arg_catalog * OUTPUT_CATALOG;
1069
1070 r = output_journal(stdout, j, arg_output, 0, flags);
1071 if (r < 0)
1072 goto finish;
1073
1074 need_seek = true;
1075 n_shown++;
1076 }
1077
1078 if (!arg_follow)
1079 break;
1080
1081 r = sd_journal_wait(j, (uint64_t) -1);
1082 if (r < 0) {
1083 log_error("Couldn't wait for journal event: %s", strerror(-r));
1084 goto finish;
1085 }
1086 }
1087
1088 finish:
1089 if (j)
1090 sd_journal_close(j);
1091
1092 pager_close();
1093
1094 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1095 }