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