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