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