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