journalctl.c 38.1 KB
Newer Older
1
2
3
4
5
6
7
8
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/

/***
  This file is part of systemd.

  Copyright 2011 Lennart Poettering

  systemd is free software; you can redistribute it and/or modify it
9
10
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
11
12
13
14
15
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  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
20
21
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

22
#include <locale.h>
23
24
25
#include <fcntl.h>
#include <errno.h>
#include <stddef.h>
26
27
28
29
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
30
#include <sys/poll.h>
31
#include <time.h>
32
#include <getopt.h>
33
#include <signal.h>
34
#include <sys/stat.h>
35
36
#include <sys/ioctl.h>
#include <linux/fs.h>
37

38
39
#include <systemd/sd-journal.h>

40
#include "log.h"
41
#include "util.h"
42
#include "path-util.h"
43
44
#include "build.h"
#include "pager.h"
45
#include "logs-show.h"
46
#include "strv.h"
47
#include "journal-internal.h"
48
#include "journal-def.h"
49
#include "journal-verify.h"
50
#include "journal-authenticate.h"
51
#include "journal-qrcode.h"
52
#include "fsprg.h"
53
#include "unit-name.h"
54
#include "catalog.h"
55

56
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
57

58
static OutputMode arg_output = OUTPUT_SHORT;
59
static bool arg_follow = false;
60
static bool arg_full = false;
61
static bool arg_all = false;
62
static bool arg_no_pager = false;
63
static int arg_lines = -1;
64
static bool arg_no_tail = false;
65
static bool arg_quiet = false;
66
static bool arg_merge = false;
67
static bool arg_this_boot = false;
68
static const char *arg_cursor = NULL;
69
static const char *arg_directory = NULL;
70
static int arg_priorities = 0xFF;
71
static const char *arg_verify_key = NULL;
72
#ifdef HAVE_GCRYPT
73
static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
74
#endif
75
76
static usec_t arg_since, arg_until;
static bool arg_since_set = false, arg_until_set = false;
77
static const char *arg_unit = NULL;
78
static const char *arg_field = NULL;
79
static bool arg_catalog = false;
80

81
82
83
84
static enum {
        ACTION_SHOW,
        ACTION_NEW_ID128,
        ACTION_PRINT_HEADER,
85
        ACTION_SETUP_KEYS,
86
87
        ACTION_VERIFY,
        ACTION_DISK_USAGE,
88
89
        ACTION_LIST_CATALOG,
        ACTION_UPDATE_CATALOG
90
91
} arg_action = ACTION_SHOW;

92
93
static int help(void) {

94
        printf("%s [OPTIONS...] [MATCHES...]\n\n"
95
96
               "Query the journal.\n\n"
               "Flags:\n"
97
98
               "     --since=DATE        Start showing entries newer or of the specified date\n"
               "     --until=DATE        Stop showing entries older or of the specified date\n"
99
               "  -c --cursor=CURSOR     Start showing entries from specified cursor\n"
100
101
               "  -b --this-boot         Show data only from current boot\n"
               "  -u --unit=UNIT         Show data only from the specified unit\n"
102
               "  -p --priority=RANGE    Show only messages within the specified priority range\n"
103
               "  -f --follow            Follow journal\n"
104
               "  -n --lines[=INTEGER]   Number of journal entries to show\n"
105
106
               "     --no-tail           Show all lines, even in follow mode\n"
               "  -o --output=STRING     Change journal output mode (short, short-monotonic,\n"
107
               "                         verbose, export, json, json-pretty, json-sse, cat)\n"
108
               "  -x --catalog           Add message explanations where available\n"
109
               "     --full              Do not ellipsize fields\n"
110
               "  -a --all               Show all fields, including long and unprintable\n"
111
               "  -q --quiet             Don't show privilege warning\n"
112
               "     --no-pager          Do not pipe output into a pager\n"
113
               "  -m --merge             Show entries from all available journals\n"
114
               "  -D --directory=PATH    Show journal files from directory\n"
115
116
117
118
119
120
121
#ifdef HAVE_GCRYPT
               "     --interval=TIME     Time interval for changing the FSS sealing key\n"
               "     --verify-key=KEY    Specify FSS verification key\n"
#endif
               "\nCommands:\n"
               "  -h --help              Show this help\n"
               "     --version           Show package version\n"
122
123
               "     --new-id128         Generate a new 128 Bit ID\n"
               "     --header            Show journal header information\n"
124
               "     --disk-usage        Show total disk usage\n"
125
               "  -F --field=FIELD       List all values a certain field takes\n"
126
127
               "     --list-catalog      Show message IDs of all entries in the message catalog\n"
               "     --update-catalog    Update the message catalog database\n"
128
#ifdef HAVE_GCRYPT
129
130
               "     --setup-keys        Generate new FSS key pair\n"
               "     --verify            Verify journal file consistency\n"
131
132
#endif
               , program_invocation_short_name);
133
134
135
136
137
138
139
140

        return 0;
}

static int parse_argv(int argc, char *argv[]) {

        enum {
                ARG_VERSION = 0x100,
141
                ARG_NO_PAGER,
142
                ARG_NO_TAIL,
143
                ARG_NEW_ID128,
144
                ARG_HEADER,
145
                ARG_FULL,
146
                ARG_SETUP_KEYS,
147
                ARG_INTERVAL,
148
                ARG_VERIFY,
149
                ARG_VERIFY_KEY,
150
151
                ARG_DISK_USAGE,
                ARG_SINCE,
152
153
154
                ARG_UNTIL,
                ARG_LIST_CATALOG,
                ARG_UPDATE_CATALOG
155
156
157
        };

        static const struct option options[] = {
158
159
160
161
162
163
                { "help",         no_argument,       NULL, 'h'              },
                { "version" ,     no_argument,       NULL, ARG_VERSION      },
                { "no-pager",     no_argument,       NULL, ARG_NO_PAGER     },
                { "follow",       no_argument,       NULL, 'f'              },
                { "output",       required_argument, NULL, 'o'              },
                { "all",          no_argument,       NULL, 'a'              },
164
                { "full",         no_argument,       NULL, ARG_FULL         },
165
                { "lines",        optional_argument, NULL, 'n'              },
166
167
168
                { "no-tail",      no_argument,       NULL, ARG_NO_TAIL      },
                { "new-id128",    no_argument,       NULL, ARG_NEW_ID128    },
                { "quiet",        no_argument,       NULL, 'q'              },
169
                { "merge",        no_argument,       NULL, 'm'              },
170
171
172
                { "this-boot",    no_argument,       NULL, 'b'              },
                { "directory",    required_argument, NULL, 'D'              },
                { "header",       no_argument,       NULL, ARG_HEADER       },
173
                { "priority",     required_argument, NULL, 'p'              },
174
175
176
177
                { "setup-keys",   no_argument,       NULL, ARG_SETUP_KEYS   },
                { "interval",     required_argument, NULL, ARG_INTERVAL     },
                { "verify",       no_argument,       NULL, ARG_VERIFY       },
                { "verify-key",   required_argument, NULL, ARG_VERIFY_KEY   },
178
                { "disk-usage",   no_argument,       NULL, ARG_DISK_USAGE   },
179
                { "cursor",       required_argument, NULL, 'c'              },
180
181
                { "since",        required_argument, NULL, ARG_SINCE        },
                { "until",        required_argument, NULL, ARG_UNTIL        },
182
                { "unit",         required_argument, NULL, 'u'              },
183
                { "field",        required_argument, NULL, 'F'              },
184
185
186
                { "catalog",      no_argument,       NULL, 'x'              },
                { "list-catalog", no_argument,       NULL, ARG_LIST_CATALOG },
                { "update-catalog",no_argument,      NULL, ARG_UPDATE_CATALOG },
187
                { NULL,           0,                 NULL, 0                }
188
189
        };

Lennart Poettering's avatar
Lennart Poettering committed
190
        int c, r;
191
192
193
194

        assert(argc >= 0);
        assert(argv);

195
        while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) {
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

                switch (c) {

                case 'h':
                        help();
                        return 0;

                case ARG_VERSION:
                        puts(PACKAGE_STRING);
                        puts(SYSTEMD_FEATURES);
                        return 0;

                case ARG_NO_PAGER:
                        arg_no_pager = true;
                        break;

                case 'f':
                        arg_follow = true;
                        break;

                case 'o':
217
                        arg_output = output_mode_from_string(optarg);
218
                        if (arg_output < 0) {
219
                                log_error("Unknown output format '%s'.", optarg);
220
221
                                return -EINVAL;
                        }
222

223
224
225
226
227
228
229
                        if (arg_output == OUTPUT_EXPORT ||
                            arg_output == OUTPUT_JSON ||
                            arg_output == OUTPUT_JSON_PRETTY ||
                            arg_output == OUTPUT_JSON_SSE ||
                            arg_output == OUTPUT_CAT)
                                arg_quiet = true;

230
231
                        break;

232
233
234
235
                case ARG_FULL:
                        arg_full = true;
                        break;

236
                case 'a':
237
                        arg_all = true;
238
239
                        break;

Lennart Poettering's avatar
Lennart Poettering committed
240
                case 'n':
241
                        if (optarg) {
242
243
                                r = safe_atoi(optarg, &arg_lines);
                                if (r < 0 || arg_lines < 0) {
244
245
246
247
248
249
                                        log_error("Failed to parse lines '%s'", optarg);
                                        return -EINVAL;
                                }
                        } else
                                arg_lines = 10;

Lennart Poettering's avatar
Lennart Poettering committed
250
251
                        break;

252
253
254
255
                case ARG_NO_TAIL:
                        arg_no_tail = true;
                        break;

256
                case ARG_NEW_ID128:
257
                        arg_action = ACTION_NEW_ID128;
258
259
                        break;

260
261
                case 'q':
                        arg_quiet = true;
262
                        break;
263

264
265
                case 'm':
                        arg_merge = true;
266
267
                        break;

268
269
270
271
                case 'b':
                        arg_this_boot = true;
                        break;

272
273
274
275
                case 'D':
                        arg_directory = optarg;
                        break;

276
277
278
279
                case 'c':
                        arg_cursor = optarg;
                        break;

280
                case ARG_HEADER:
281
282
283
                        arg_action = ACTION_PRINT_HEADER;
                        break;

284
285
286
287
                case ARG_VERIFY:
                        arg_action = ACTION_VERIFY;
                        break;

288
289
290
291
                case ARG_DISK_USAGE:
                        arg_action = ACTION_DISK_USAGE;
                        break;

292
#ifdef HAVE_GCRYPT
293
294
                case ARG_SETUP_KEYS:
                        arg_action = ACTION_SETUP_KEYS;
295
296
                        break;

297

298
                case ARG_VERIFY_KEY:
299
                        arg_action = ACTION_VERIFY;
300
                        arg_verify_key = optarg;
301
                        arg_merge = false;
302
303
                        break;

304
305
306
307
                case ARG_INTERVAL:
                        r = parse_usec(optarg, &arg_interval);
                        if (r < 0 || arg_interval <= 0) {
                                log_error("Failed to parse sealing key change interval: %s", optarg);
308
309
310
                                return -EINVAL;
                        }
                        break;
311
312
313
314
315
316
317
#else
                case ARG_SETUP_KEYS:
                case ARG_VERIFY_KEY:
                case ARG_INTERVAL:
                        log_error("Forward-secure sealing not available.");
                        return -ENOTSUP;
#endif
318

319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
                case 'p': {
                        const char *dots;

                        dots = strstr(optarg, "..");
                        if (dots) {
                                char *a;
                                int from, to, i;

                                /* a range */
                                a = strndup(optarg, dots - optarg);
                                if (!a)
                                        return log_oom();

                                from = log_level_from_string(a);
                                to = log_level_from_string(dots + 2);
                                free(a);

                                if (from < 0 || to < 0) {
                                        log_error("Failed to parse log level range %s", optarg);
                                        return -EINVAL;
                                }

                                arg_priorities = 0;

                                if (from < to) {
                                        for (i = from; i <= to; i++)
                                                arg_priorities |= 1 << i;
                                } else {
                                        for (i = to; i <= from; i++)
                                                arg_priorities |= 1 << i;
                                }

                        } else {
                                int p, i;

                                p = log_level_from_string(optarg);
                                if (p < 0) {
                                        log_error("Unknown log level %s", optarg);
                                        return -EINVAL;
                                }

                                arg_priorities = 0;

                                for (i = 0; i <= p; i++)
                                        arg_priorities |= 1 << i;
                        }

                        break;
                }

369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
                case ARG_SINCE:
                        r = parse_timestamp(optarg, &arg_since);
                        if (r < 0) {
                                log_error("Failed to parse timestamp: %s", optarg);
                                return -EINVAL;
                        }
                        arg_since_set = true;
                        break;

                case ARG_UNTIL:
                        r = parse_timestamp(optarg, &arg_until);
                        if (r < 0) {
                                log_error("Failed to parse timestamp: %s", optarg);
                                return -EINVAL;
                        }
                        arg_until_set = true;
                        break;

387
388
389
390
                case 'u':
                        arg_unit = optarg;
                        break;

391
392
393
                case '?':
                        return -EINVAL;

394
395
396
397
                case 'F':
                        arg_field = optarg;
                        break;

398
399
400
401
402
403
404
405
406
407
408
409
                case 'x':
                        arg_catalog = true;
                        break;

                case ARG_LIST_CATALOG:
                        arg_action = ACTION_LIST_CATALOG;
                        break;

                case ARG_UPDATE_CATALOG:
                        arg_action = ACTION_UPDATE_CATALOG;
                        break;

410
411
412
413
414
415
                default:
                        log_error("Unknown option code %c", c);
                        return -EINVAL;
                }
        }

416
        if (arg_follow && !arg_no_tail && arg_lines < 0)
417
418
                arg_lines = 10;

419
420
421
422
423
424
425
426
427
428
        if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
                log_error("--since= must be before --until=.");
                return -EINVAL;
        }

        if (arg_cursor && arg_since_set) {
                log_error("Please specify either --since= or --cursor=, not both.");
                return -EINVAL;
        }

429
430
431
        return 1;
}

432
static int generate_new_id128(void) {
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
        sd_id128_t id;
        int r;
        unsigned i;

        r = sd_id128_randomize(&id);
        if (r < 0) {
                log_error("Failed to generate ID: %s", strerror(-r));
                return r;
        }

        printf("As string:\n"
               SD_ID128_FORMAT_STR "\n\n"
               "As UUID:\n"
               "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
               "As macro:\n"
              "#define MESSAGE_XYZ SD_ID128_MAKE(",
               SD_ID128_FORMAT_VAL(id),
               SD_ID128_FORMAT_VAL(id));

        for (i = 0; i < 16; i++)
                printf("%02x%s", id.bytes[i], i != 15 ? "," : "");

        fputs(")\n", stdout);

        return 0;
}

460
461
462
static int add_matches(sd_journal *j, char **args) {
        char **i;
        int r;
463

464
        assert(j);
465

466
        STRV_FOREACH(i, args) {
467

468
469
470
                if (streq(*i, "+"))
                        r = sd_journal_add_disjunction(j);
                else if (path_is_absolute(*i)) {
471
                        char *p, *t = NULL;
472
                        const char *path;
473
                        struct stat st;
474

475
476
                        p = canonicalize_file_name(*i);
                        path = p ? p : *i;
477
478
479
480

                        if (stat(path, &st) < 0)  {
                                free(p);
                                log_error("Couldn't stat file: %m");
481
                                return -errno;
482
483
                        }

484
                        if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
485
                                t = strappend("_EXE=", path);
486
487
488
489
490
                        else if (S_ISCHR(st.st_mode))
                                asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
                        else if (S_ISBLK(st.st_mode))
                                asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
                        else {
491
                                free(p);
492
                                log_error("File is not a device node, regular file or is not executable: %s", *i);
493
                                return -EINVAL;
494
                        }
495
496

                        free(p);
497
498
499
500
501
502

                        if (!t)
                                return log_oom();

                        r = sd_journal_add_match(j, t, 0);
                        free(t);
503
                } else
504
                        r = sd_journal_add_match(j, *i, 0);
505

506
                if (r < 0) {
507
                        log_error("Failed to add match '%s': %s", *i, strerror(-r));
508
                        return r;
509
510
511
                }
        }

512
513
514
515
516
517
518
519
        return 0;
}

static int add_this_boot(sd_journal *j) {
        char match[9+32+1] = "_BOOT_ID=";
        sd_id128_t boot_id;
        int r;

520
521
        assert(j);

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
        if (!arg_this_boot)
                return 0;

        r = sd_id128_get_boot(&boot_id);
        if (r < 0) {
                log_error("Failed to get boot id: %s", strerror(-r));
                return r;
        }

        sd_id128_to_string(boot_id, match + 9);
        r = sd_journal_add_match(j, match, strlen(match));
        if (r < 0) {
                log_error("Failed to add match: %s", strerror(-r));
                return r;
        }

        return 0;
}

541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
static int add_unit(sd_journal *j) {
        _cleanup_free_ char *m = NULL, *u = NULL;
        int r;

        assert(j);

        if (isempty(arg_unit))
                return 0;

        u = unit_name_mangle(arg_unit);
        if (!u)
                return log_oom();

        m = strappend("_SYSTEMD_UNIT=", u);
        if (!m)
                return log_oom();

        r = sd_journal_add_match(j, m, strlen(m));
        if (r < 0) {
                log_error("Failed to add match: %s", strerror(-r));
                return r;
        }

        return 0;
}

567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
static int add_priorities(sd_journal *j) {
        char match[] = "PRIORITY=0";
        int i, r;

        assert(j);

        if (arg_priorities == 0xFF)
                return 0;

        for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
                if (arg_priorities & (1 << i)) {
                        match[sizeof(match)-2] = '0' + i;

                        r = sd_journal_add_match(j, match, strlen(match));
                        if (r < 0) {
                                log_error("Failed to add match: %s", strerror(-r));
                                return r;
                        }
                }

        return 0;
}

590
591
592
593
594
static int setup_keys(void) {
#ifdef HAVE_GCRYPT
        size_t mpk_size, seed_size, state_size, i;
        uint8_t *mpk, *seed, *state;
        ssize_t l;
595
        int fd = -1, r, attr = 0;
596
597
        sd_id128_t machine, boot;
        char *p = NULL, *k = NULL;
598
        struct FSSHeader h;
599
        uint64_t n;
600
601
602
603
604
605
606
607
608
609
610
611
612

        r = sd_id128_get_machine(&machine);
        if (r < 0) {
                log_error("Failed to get machine ID: %s", strerror(-r));
                return r;
        }

        r = sd_id128_get_boot(&boot);
        if (r < 0) {
                log_error("Failed to get boot ID: %s", strerror(-r));
                return r;
        }

613
        if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
614
615
616
617
                     SD_ID128_FORMAT_VAL(machine)) < 0)
                return log_oom();

        if (access(p, F_OK) >= 0) {
618
                log_error("Sealing key file %s exists already.", p);
619
620
621
622
                r = -EEXIST;
                goto finish;
        }

623
        if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
                     SD_ID128_FORMAT_VAL(machine)) < 0) {
                r = log_oom();
                goto finish;
        }

        mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
        mpk = alloca(mpk_size);

        seed_size = FSPRG_RECOMMENDED_SEEDLEN;
        seed = alloca(seed_size);

        state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
        state = alloca(state_size);

        fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
        if (fd < 0) {
                log_error("Failed to open /dev/random: %m");
                r = -errno;
                goto finish;
        }

        log_info("Generating seed...");
        l = loop_read(fd, seed, seed_size, true);
        if (l < 0 || (size_t) l != seed_size) {
                log_error("Failed to read random seed: %s", strerror(EIO));
                r = -EIO;
                goto finish;
        }

        log_info("Generating key pair...");
        FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);

656
        log_info("Generating sealing key...");
657
658
        FSPRG_GenState0(state, mpk, seed, seed_size);

659
660
        assert(arg_interval > 0);

661
        n = now(CLOCK_REALTIME);
662
        n /= arg_interval;
663
664
665
666
667
668
669
670
671

        close_nointr_nofail(fd);
        fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
        if (fd < 0) {
                log_error("Failed to open %s: %m", k);
                r = -errno;
                goto finish;
        }

672
673
674
675
676
677
678
679
680
681
        /* Enable secure remove, exclusion from dump, synchronous
         * writing and in-place updating */
        if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
                log_warning("FS_IOC_GETFLAGS failed: %m");

        attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;

        if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
                log_warning("FS_IOC_SETFLAGS failed: %m");

682
683
684
685
686
        zero(h);
        memcpy(h.signature, "KSHHRHLP", 8);
        h.machine_id = machine;
        h.boot_id = boot;
        h.header_size = htole64(sizeof(h));
687
688
689
690
        h.start_usec = htole64(n * arg_interval);
        h.interval_usec = htole64(arg_interval);
        h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
        h.fsprg_state_size = htole64(state_size);
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

        l = loop_write(fd, &h, sizeof(h), false);
        if (l < 0 || (size_t) l != sizeof(h)) {
                log_error("Failed to write header: %s", strerror(EIO));
                r = -EIO;
                goto finish;
        }

        l = loop_write(fd, state, state_size, false);
        if (l < 0 || (size_t) l != state_size) {
                log_error("Failed to write state: %s", strerror(EIO));
                r = -EIO;
                goto finish;
        }

        if (link(k, p) < 0) {
                log_error("Failed to link file: %m");
                r = -errno;
                goto finish;
        }

712
        if (on_tty()) {
713
714
                fprintf(stderr,
                        "\n"
715
                        "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
716
717
                        "the following local file. This key file is automatically updated when the\n"
                        "sealing key is advanced. It should not be used on multiple hosts.\n"
718
719
720
                        "\n"
                        "\t%s\n"
                        "\n"
721
722
                        "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
                        "at a safe location and should not be saved locally on disk.\n"
723
724
725
726
727
728
729
730
731
                        "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
                fflush(stderr);
        }
        for (i = 0; i < seed_size; i++) {
                if (i > 0 && i % 3 == 0)
                        putchar('-');
                printf("%02x", ((uint8_t*) seed)[i]);
        }

732
733
        printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);

734
        if (on_tty()) {
735
                char tsb[FORMAT_TIMESPAN_MAX], *hn;
736

737
738
739
740
                fprintf(stderr,
                        ANSI_HIGHLIGHT_OFF "\n"
                        "The sealing key is automatically changed every %s.\n",
                        format_timespan(tsb, sizeof(tsb), arg_interval));
741
742
743
744
745

                hn = gethostname_malloc();

                if (hn) {
                        hostname_cleanup(hn);
746
                        fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
747
                } else
748
                        fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
749
750

#ifdef HAVE_QRENCODE
751
                /* If this is not an UTF-8 system don't print any QR codes */
Michal Schmidt's avatar
Michal Schmidt committed
752
                if (is_locale_utf8()) {
753
754
755
                        fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
                        print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
                }
756
757
#endif
                free(hn);
758
        }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

        r = 0;

finish:
        if (fd >= 0)
                close_nointr_nofail(fd);

        if (k) {
                unlink(k);
                free(k);
        }

        free(p);

        return r;
#else
775
776
        log_error("Forward-secure sealing not available.");
        return -ENOTSUP;
777
778
779
#endif
}

780
781
782
783
784
785
786
static int verify(sd_journal *j) {
        int r = 0;
        Iterator i;
        JournalFile *f;

        assert(j);

787
788
        log_show_color(true);

789
790
        HASHMAP_FOREACH(f, j->files, i) {
                int k;
791
                usec_t first, validated, last;
792

793
#ifdef HAVE_GCRYPT
794
                if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
795
                        log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
796
#endif
797

798
                k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
799
                if (k == -EINVAL) {
800
                        /* If the key was invalid give up right-away. */
801
802
                        return k;
                } else if (k < 0) {
803
                        log_warning("FAIL: %s (%s)", f->path, strerror(-k));
804
                        r = k;
805
806
                } else {
                        char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
807
                        log_info("PASS: %s", f->path);
808

809
                        if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
810
                                if (validated > 0) {
811
                                        log_info("=> Validated from %s to %s, final %s entries not sealed.",
812
813
814
815
                                                 format_timestamp(a, sizeof(a), first),
                                                 format_timestamp(b, sizeof(b), validated),
                                                 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
                                } else if (last > 0)
816
                                        log_info("=> No sealing yet, %s of entries not sealed.",
817
                                                 format_timespan(c, sizeof(c), last - first));
818
819
820
                                else
                                        log_info("=> No sealing yet, no entries in file.");
                        }
821
                }
822
823
824
825
826
        }

        return r;
}

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
static int access_check(void) {

#ifdef HAVE_ACL
        if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
                log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
                return -EACCES;
        }

        if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
                log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
#else
        if (geteuid() != 0 && in_group("adm") <= 0) {
                log_error("No access to messages. Only users in the group 'adm' can see messages.");
                return -EACCES;
        }
#endif

        return 0;
}

847
848
849
850
int main(int argc, char *argv[]) {
        int r;
        sd_journal *j = NULL;
        bool need_seek = false;
851
        sd_id128_t previous_boot_id;
852
853
        bool previous_boot_id_valid = false, first_line = true;
        int n_shown = 0;
854

855
        setlocale(LC_ALL, "");
856
857
858
859
860
861
862
        log_parse_environment();
        log_open();

        r = parse_argv(argc, argv);
        if (r <= 0)
                goto finish;

863
864
        signal(SIGWINCH, columns_lines_cache_reset);

865
        if (arg_action == ACTION_NEW_ID128) {
866
867
868
869
                r = generate_new_id128();
                goto finish;
        }

870
871
872
873
874
        if (arg_action == ACTION_SETUP_KEYS) {
                r = setup_keys();
                goto finish;
        }

875
876
        if (arg_action == ACTION_LIST_CATALOG)  {
                r = catalog_list(stdout);
877
878
                if (r < 0)
                        log_error("Failed to list catalog: %s", strerror(-r));
879
880
881
882
883
884
885
886
                goto finish;
        }

        if (arg_action == ACTION_UPDATE_CATALOG)  {
                r = catalog_update();
                goto finish;
        }

887
888
889
890
        r = access_check();
        if (r < 0)
                goto finish;

891
892
893
        if (arg_directory)
                r = sd_journal_open_directory(&j, arg_directory, 0);
        else
894
                r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
895
896
897
898
899
        if (r < 0) {
                log_error("Failed to open journal: %s", strerror(-r));
                goto finish;
        }

900
901
902
903
904
        if (arg_action == ACTION_VERIFY) {
                r = verify(j);
                goto finish;
        }

905
        if (arg_action == ACTION_PRINT_HEADER) {
906
907
908
909
910
                journal_print_header(j);
                r = 0;
                goto finish;
        }

911
912
913
914
915
916
917
918
919
920
921
922
923
        if (arg_action == ACTION_DISK_USAGE) {
                uint64_t bytes;
                char sbytes[FORMAT_BYTES_MAX];

                r = sd_journal_get_usage(j, &bytes);
                if (r < 0)
                        goto finish;

                printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
                r = 0;
                goto finish;
        }

924
925
926
927
        r = add_this_boot(j);
        if (r < 0)
                goto finish;

928
929
930
931
        r = add_unit(j);
        if (r < 0)
                goto finish;

932
933
934
935
        r = add_matches(j, argv + optind);
        if (r < 0)
                goto finish;

936
937
938
939
        r = add_priorities(j);
        if (r < 0)
                goto finish;

940
941
942
943
944
        /* Opening the fd now means the first sd_journal_wait() will actually wait */
        r = sd_journal_get_fd(j);
        if (r < 0)
                goto finish;

945
946
947
948
949
950
951
952
953
954
955
956
957
        if (arg_field) {
                const void *data;
                size_t size;

                r = sd_journal_query_unique(j, arg_field);
                if (r < 0) {
                        log_error("Failed to query unique data objects: %s", strerror(-r));
                        goto finish;
                }

                SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
                        const void *eq;

958
                        if (arg_lines >= 0 && n_shown >= arg_lines)
959
960
                                break;

961
962
963
964
965
                        eq = memchr(data, '=', size);
                        if (eq)
                                printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
                        else
                                printf("%.*s\n", (int) size, (const char*) data);
966
967

                        n_shown ++;
968
969
970
971
972
973
                }

                r = 0;
                goto finish;
        }

974
975
        if (arg_cursor) {
                r = sd_journal_seek_cursor(j, arg_cursor);
976
                if (r < 0) {
977
                        log_error("Failed to seek to cursor: %s", strerror(-r));
978
979
980
                        goto finish;
                }

981
                r = sd_journal_next(j);
982

983
984
        } else if (arg_since_set) {
                r = sd_journal_seek_realtime_usec(j, arg_since);
985
                if (r < 0) {
986
                        log_error("Failed to seek to date: %s", strerror(-r));
987
988
989
990
                        goto finish;
                }
                r = sd_journal_next(j);

991
        } else if (arg_lines >= 0) {
Lennart Poettering's avatar
Lennart Poettering committed
992
993
994
995
996
997
998
                r = sd_journal_seek_tail(j);
                if (r < 0) {
                        log_error("Failed to seek to tail: %s", strerror(-r));
                        goto finish;
                }

                r = sd_journal_previous_skip(j, arg_lines);
999

Lennart Poettering's avatar
Lennart Poettering committed
1000
        } else {
For faster browsing, not all history is shown. View entire blame