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