]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
journalctl: add --reverse option to show the newest lines first
[thirdparty/systemd.git] / src / journal / journalctl.c
CommitLineData
87d2c1ff
LP
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
5430f7f2
LP
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
87d2c1ff
LP
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
5430f7f2 16 Lesser General Public License for more details.
87d2c1ff 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a9cdc94f 22#include <locale.h>
87d2c1ff
LP
23#include <fcntl.h>
24#include <errno.h>
25#include <stddef.h>
3fbf9cbb
LP
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <stdlib.h>
50f20cfd 30#include <sys/poll.h>
72f59706 31#include <time.h>
0d43c694 32#include <getopt.h>
585314e8 33#include <signal.h>
50940700 34#include <sys/stat.h>
f982e6f7
LP
35#include <sys/ioctl.h>
36#include <linux/fs.h>
87d2c1ff 37
81527be1
LP
38#include <systemd/sd-journal.h>
39
3fbf9cbb 40#include "log.h"
72f59706 41#include "util.h"
e5124088 42#include "path-util.h"
0d43c694
LP
43#include "build.h"
44#include "pager.h"
86aa7ba4 45#include "logs-show.h"
a963990f 46#include "strv.h"
dca6219e 47#include "journal-internal.h"
7560fffc 48#include "journal-def.h"
0284adc6 49#include "journal-verify.h"
4da416aa 50#include "journal-authenticate.h"
f6a971bc 51#include "journal-qrcode.h"
4da416aa 52#include "fsprg.h"
c3f60ec5 53#include "unit-name.h"
d4205751 54#include "catalog.h"
7560fffc 55
baed47c3 56#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 57
df50185b 58static OutputMode arg_output = OUTPUT_SHORT;
72f59706 59static bool arg_follow = false;
e3657ecd 60static bool arg_full = false;
cd4b13e0 61static bool arg_all = false;
0d43c694 62static bool arg_no_pager = false;
67e04a48 63static int arg_lines = -1;
e91af489 64static bool arg_no_tail = false;
43673799 65static bool arg_quiet = false;
9e8a535f 66static bool arg_merge = false;
59cea26a 67static bool arg_this_boot = false;
8f14c832 68static const char *arg_cursor = NULL;
a963990f 69static const char *arg_directory = NULL;
941e990d 70static int arg_priorities = 0xFF;
baed47c3 71static const char *arg_verify_key = NULL;
feb12d3e 72#ifdef HAVE_GCRYPT
baed47c3 73static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
feb12d3e 74#endif
cfbc22ab
LP
75static usec_t arg_since, arg_until;
76static bool arg_since_set = false, arg_until_set = false;
c3f60ec5 77static const char *arg_unit = NULL;
3c1668da 78static const char *arg_field = NULL;
d4205751 79static bool arg_catalog = false;
d89d6c86 80static bool arg_reverse = false;
50f20cfd 81
7560fffc
LP
82static enum {
83 ACTION_SHOW,
84 ACTION_NEW_ID128,
85 ACTION_PRINT_HEADER,
beec0085 86 ACTION_SETUP_KEYS,
a1a03e30
LP
87 ACTION_VERIFY,
88 ACTION_DISK_USAGE,
d4205751
LP
89 ACTION_LIST_CATALOG,
90 ACTION_UPDATE_CATALOG
7560fffc
LP
91} arg_action = ACTION_SHOW;
92
0d43c694
LP
93static int help(void) {
94
cd4b13e0 95 printf("%s [OPTIONS...] [MATCHES...]\n\n"
15119c16
LP
96 "Query the journal.\n\n"
97 "Flags:\n"
cfbc22ab
LP
98 " --since=DATE Start showing entries newer or of the specified date\n"
99 " --until=DATE Stop showing entries older or of the specified date\n"
cd4b13e0 100 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
c3f60ec5
LP
101 " -b --this-boot Show data only from current boot\n"
102 " -u --unit=UNIT Show data only from the specified unit\n"
cd4b13e0 103 " -p --priority=RANGE Show only messages within the specified priority range\n"
baed47c3 104 " -f --follow Follow journal\n"
1705594f 105 " -n --lines[=INTEGER] Number of journal entries to show\n"
baed47c3 106 " --no-tail Show all lines, even in follow mode\n"
d89d6c86 107 " -r --reverse Show the newest entries first\n"
baed47c3 108 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
48383c25 109 " verbose, export, json, json-pretty, json-sse, cat)\n"
d4205751 110 " -x --catalog Add message explanations where available\n"
e3657ecd 111 " --full Do not ellipsize fields\n"
c3f60ec5 112 " -a --all Show all fields, including long and unprintable\n"
baed47c3 113 " -q --quiet Don't show privilege warning\n"
c3f60ec5 114 " --no-pager Do not pipe output into a pager\n"
9e8a535f 115 " -m --merge Show entries from all available journals\n"
baed47c3 116 " -D --directory=PATH Show journal files from directory\n"
15119c16
LP
117#ifdef HAVE_GCRYPT
118 " --interval=TIME Time interval for changing the FSS sealing key\n"
119 " --verify-key=KEY Specify FSS verification key\n"
120#endif
121 "\nCommands:\n"
122 " -h --help Show this help\n"
123 " --version Show package version\n"
baed47c3
LP
124 " --new-id128 Generate a new 128 Bit ID\n"
125 " --header Show journal header information\n"
a1a03e30 126 " --disk-usage Show total disk usage\n"
15119c16 127 " -F --field=FIELD List all values a certain field takes\n"
d4205751
LP
128 " --list-catalog Show message IDs of all entries in the message catalog\n"
129 " --update-catalog Update the message catalog database\n"
feb12d3e 130#ifdef HAVE_GCRYPT
baed47c3 131 " --setup-keys Generate new FSS key pair\n"
baed47c3 132 " --verify Verify journal file consistency\n"
feb12d3e
LP
133#endif
134 , program_invocation_short_name);
0d43c694
LP
135
136 return 0;
137}
138
139static int parse_argv(int argc, char *argv[]) {
140
141 enum {
142 ARG_VERSION = 0x100,
e91af489 143 ARG_NO_PAGER,
55ee336c 144 ARG_NO_TAIL,
dca6219e 145 ARG_NEW_ID128,
7560fffc 146 ARG_HEADER,
e3657ecd 147 ARG_FULL,
beec0085 148 ARG_SETUP_KEYS,
baed47c3 149 ARG_INTERVAL,
4da416aa 150 ARG_VERIFY,
a1a03e30 151 ARG_VERIFY_KEY,
cfbc22ab
LP
152 ARG_DISK_USAGE,
153 ARG_SINCE,
d4205751
LP
154 ARG_UNTIL,
155 ARG_LIST_CATALOG,
156 ARG_UPDATE_CATALOG
0d43c694
LP
157 };
158
159 static const struct option options[] = {
baed47c3
LP
160 { "help", no_argument, NULL, 'h' },
161 { "version" , no_argument, NULL, ARG_VERSION },
162 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
163 { "follow", no_argument, NULL, 'f' },
164 { "output", required_argument, NULL, 'o' },
165 { "all", no_argument, NULL, 'a' },
e3657ecd 166 { "full", no_argument, NULL, ARG_FULL },
1705594f 167 { "lines", optional_argument, NULL, 'n' },
baed47c3
LP
168 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
169 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
170 { "quiet", no_argument, NULL, 'q' },
9e8a535f 171 { "merge", no_argument, NULL, 'm' },
baed47c3
LP
172 { "this-boot", no_argument, NULL, 'b' },
173 { "directory", required_argument, NULL, 'D' },
174 { "header", no_argument, NULL, ARG_HEADER },
71c01596 175 { "priority", required_argument, NULL, 'p' },
baed47c3
LP
176 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
177 { "interval", required_argument, NULL, ARG_INTERVAL },
178 { "verify", no_argument, NULL, ARG_VERIFY },
179 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
a1a03e30 180 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
ad9eafab 181 { "cursor", required_argument, NULL, 'c' },
cfbc22ab
LP
182 { "since", required_argument, NULL, ARG_SINCE },
183 { "until", required_argument, NULL, ARG_UNTIL },
c3f60ec5 184 { "unit", required_argument, NULL, 'u' },
15119c16 185 { "field", required_argument, NULL, 'F' },
d4205751
LP
186 { "catalog", no_argument, NULL, 'x' },
187 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
188 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
d89d6c86 189 { "reverse", no_argument, NULL, 'r' },
baed47c3 190 { NULL, 0, NULL, 0 }
0d43c694
LP
191 };
192
2100675e 193 int c, r;
0d43c694
LP
194
195 assert(argc >= 0);
196 assert(argv);
197
d89d6c86 198 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
0d43c694
LP
199
200 switch (c) {
201
202 case 'h':
203 help();
204 return 0;
205
206 case ARG_VERSION:
207 puts(PACKAGE_STRING);
0d43c694
LP
208 puts(SYSTEMD_FEATURES);
209 return 0;
210
211 case ARG_NO_PAGER:
212 arg_no_pager = true;
213 break;
214
215 case 'f':
216 arg_follow = true;
217 break;
218
219 case 'o':
1705594f 220 arg_output = output_mode_from_string(optarg);
df50185b 221 if (arg_output < 0) {
edfb521a 222 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
223 return -EINVAL;
224 }
df50185b 225
edfb521a
ZJS
226 if (arg_output == OUTPUT_EXPORT ||
227 arg_output == OUTPUT_JSON ||
228 arg_output == OUTPUT_JSON_PRETTY ||
229 arg_output == OUTPUT_JSON_SSE ||
230 arg_output == OUTPUT_CAT)
231 arg_quiet = true;
232
0d43c694
LP
233 break;
234
e3657ecd
ZJS
235 case ARG_FULL:
236 arg_full = true;
237 break;
238
0d43c694 239 case 'a':
cd4b13e0 240 arg_all = true;
0d43c694
LP
241 break;
242
2100675e 243 case 'n':
1705594f 244 if (optarg) {
67e04a48
ZJS
245 r = safe_atoi(optarg, &arg_lines);
246 if (r < 0 || arg_lines < 0) {
1705594f
LP
247 log_error("Failed to parse lines '%s'", optarg);
248 return -EINVAL;
249 }
96088db0
LP
250 } else {
251 int n;
252
253 /* Hmm, no argument? Maybe the next
254 * word on the command line is
255 * supposed to be the argument? Let's
256 * see if there is one, and is
257 * parsable as a positive
258 * integer... */
259
260 if (optind < argc &&
261 safe_atoi(argv[optind], &n) >= 0 &&
262 n >= 0) {
263
264 arg_lines = n;
265 optind++;
266 } else
267 arg_lines = 10;
268 }
1705594f 269
2100675e
LP
270 break;
271
e91af489
LP
272 case ARG_NO_TAIL:
273 arg_no_tail = true;
274 break;
275
39f7f5c1 276 case ARG_NEW_ID128:
7560fffc 277 arg_action = ACTION_NEW_ID128;
55ee336c
LP
278 break;
279
43673799
LP
280 case 'q':
281 arg_quiet = true;
490e567d 282 break;
43673799 283
9e8a535f
LP
284 case 'm':
285 arg_merge = true;
2bd3c38a
LP
286 break;
287
59cea26a
LP
288 case 'b':
289 arg_this_boot = true;
290 break;
291
a963990f
LP
292 case 'D':
293 arg_directory = optarg;
294 break;
295
8f14c832
LP
296 case 'c':
297 arg_cursor = optarg;
298 break;
299
dca6219e 300 case ARG_HEADER:
7560fffc
LP
301 arg_action = ACTION_PRINT_HEADER;
302 break;
303
feb12d3e
LP
304 case ARG_VERIFY:
305 arg_action = ACTION_VERIFY;
306 break;
307
a1a03e30
LP
308 case ARG_DISK_USAGE:
309 arg_action = ACTION_DISK_USAGE;
310 break;
311
feb12d3e 312#ifdef HAVE_GCRYPT
7560fffc
LP
313 case ARG_SETUP_KEYS:
314 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
315 break;
316
beec0085 317
baed47c3 318 case ARG_VERIFY_KEY:
4da416aa 319 arg_action = ACTION_VERIFY;
baed47c3 320 arg_verify_key = optarg;
9e8a535f 321 arg_merge = false;
4da416aa
LP
322 break;
323
baed47c3
LP
324 case ARG_INTERVAL:
325 r = parse_usec(optarg, &arg_interval);
326 if (r < 0 || arg_interval <= 0) {
327 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
328 return -EINVAL;
329 }
330 break;
feb12d3e
LP
331#else
332 case ARG_SETUP_KEYS:
333 case ARG_VERIFY_KEY:
334 case ARG_INTERVAL:
335 log_error("Forward-secure sealing not available.");
336 return -ENOTSUP;
337#endif
14d10188 338
941e990d
LP
339 case 'p': {
340 const char *dots;
341
342 dots = strstr(optarg, "..");
343 if (dots) {
344 char *a;
345 int from, to, i;
346
347 /* a range */
348 a = strndup(optarg, dots - optarg);
349 if (!a)
350 return log_oom();
351
352 from = log_level_from_string(a);
353 to = log_level_from_string(dots + 2);
354 free(a);
355
356 if (from < 0 || to < 0) {
357 log_error("Failed to parse log level range %s", optarg);
358 return -EINVAL;
359 }
360
361 arg_priorities = 0;
362
363 if (from < to) {
364 for (i = from; i <= to; i++)
365 arg_priorities |= 1 << i;
366 } else {
367 for (i = to; i <= from; i++)
368 arg_priorities |= 1 << i;
369 }
370
371 } else {
372 int p, i;
373
374 p = log_level_from_string(optarg);
375 if (p < 0) {
376 log_error("Unknown log level %s", optarg);
377 return -EINVAL;
378 }
379
380 arg_priorities = 0;
381
382 for (i = 0; i <= p; i++)
383 arg_priorities |= 1 << i;
384 }
385
386 break;
387 }
388
cfbc22ab
LP
389 case ARG_SINCE:
390 r = parse_timestamp(optarg, &arg_since);
391 if (r < 0) {
392 log_error("Failed to parse timestamp: %s", optarg);
393 return -EINVAL;
394 }
395 arg_since_set = true;
396 break;
397
398 case ARG_UNTIL:
399 r = parse_timestamp(optarg, &arg_until);
400 if (r < 0) {
401 log_error("Failed to parse timestamp: %s", optarg);
402 return -EINVAL;
403 }
404 arg_until_set = true;
405 break;
406
c3f60ec5
LP
407 case 'u':
408 arg_unit = optarg;
409 break;
410
0d43c694
LP
411 case '?':
412 return -EINVAL;
413
15119c16
LP
414 case 'F':
415 arg_field = optarg;
416 break;
417
d4205751
LP
418 case 'x':
419 arg_catalog = true;
420 break;
421
422 case ARG_LIST_CATALOG:
423 arg_action = ACTION_LIST_CATALOG;
424 break;
425
426 case ARG_UPDATE_CATALOG:
427 arg_action = ACTION_UPDATE_CATALOG;
428 break;
429
d89d6c86
LN
430 case 'r':
431 arg_reverse = true;
432 break;
433
0d43c694
LP
434 default:
435 log_error("Unknown option code %c", c);
436 return -EINVAL;
437 }
438 }
439
67e04a48 440 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
441 arg_lines = 10;
442
3ba09ee8 443 if (arg_since_set && arg_until_set && arg_since > arg_until) {
cfbc22ab
LP
444 log_error("--since= must be before --until=.");
445 return -EINVAL;
446 }
447
448 if (arg_cursor && arg_since_set) {
449 log_error("Please specify either --since= or --cursor=, not both.");
450 return -EINVAL;
451 }
452
d89d6c86
LN
453 if (arg_follow && arg_reverse) {
454 log_error("Please specify either --reverse= or --follow=, not both.");
455 return -EINVAL;
456 }
457
0d43c694
LP
458 return 1;
459}
460
39f7f5c1 461static int generate_new_id128(void) {
55ee336c
LP
462 sd_id128_t id;
463 int r;
464 unsigned i;
465
466 r = sd_id128_randomize(&id);
467 if (r < 0) {
468 log_error("Failed to generate ID: %s", strerror(-r));
469 return r;
470 }
471
472 printf("As string:\n"
473 SD_ID128_FORMAT_STR "\n\n"
474 "As UUID:\n"
475 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
476 "As macro:\n"
d489071f 477 "#define MESSAGE_XYZ SD_ID128_MAKE(",
55ee336c
LP
478 SD_ID128_FORMAT_VAL(id),
479 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
480 for (i = 0; i < 16; i++)
481 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
d489071f 482 fputs(")\n\n", stdout);
55ee336c 483
d489071f
ZJS
484 printf("As Python constant:\n"
485 ">>> import uuid\n"
486 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
487 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
488
489 return 0;
490}
491
a963990f
LP
492static int add_matches(sd_journal *j, char **args) {
493 char **i;
494 int r;
59cea26a 495
a963990f 496 assert(j);
59cea26a 497
a963990f 498 STRV_FOREACH(i, args) {
59cea26a 499
cbdca852
LP
500 if (streq(*i, "+"))
501 r = sd_journal_add_disjunction(j);
502 else if (path_is_absolute(*i)) {
b6a34514 503 char *p, *t = NULL;
e5124088 504 const char *path;
a963990f 505 struct stat st;
e5124088 506
a963990f
LP
507 p = canonicalize_file_name(*i);
508 path = p ? p : *i;
e5124088
LP
509
510 if (stat(path, &st) < 0) {
511 free(p);
512 log_error("Couldn't stat file: %m");
a963990f 513 return -errno;
e5124088
LP
514 }
515
b6a34514 516 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 517 t = strappend("_EXE=", path);
b6a34514
LP
518 else if (S_ISCHR(st.st_mode))
519 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
520 else if (S_ISBLK(st.st_mode))
521 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
522 else {
e5124088 523 free(p);
b6a34514 524 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 525 return -EINVAL;
50940700 526 }
e5124088
LP
527
528 free(p);
b6a34514
LP
529
530 if (!t)
531 return log_oom();
532
533 r = sd_journal_add_match(j, t, 0);
534 free(t);
e5124088 535 } else
cbdca852 536 r = sd_journal_add_match(j, *i, 0);
e5124088 537
de7b95cd 538 if (r < 0) {
cbdca852 539 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 540 return r;
de7b95cd
LP
541 }
542 }
543
a963990f
LP
544 return 0;
545}
546
547static int add_this_boot(sd_journal *j) {
548 char match[9+32+1] = "_BOOT_ID=";
549 sd_id128_t boot_id;
550 int r;
551
941e990d
LP
552 assert(j);
553
a963990f
LP
554 if (!arg_this_boot)
555 return 0;
556
557 r = sd_id128_get_boot(&boot_id);
558 if (r < 0) {
559 log_error("Failed to get boot id: %s", strerror(-r));
560 return r;
561 }
562
563 sd_id128_to_string(boot_id, match + 9);
564 r = sd_journal_add_match(j, match, strlen(match));
565 if (r < 0) {
566 log_error("Failed to add match: %s", strerror(-r));
567 return r;
568 }
569
570 return 0;
571}
572
c3f60ec5
LP
573static int add_unit(sd_journal *j) {
574 _cleanup_free_ char *m = NULL, *u = NULL;
575 int r;
576
577 assert(j);
578
579 if (isempty(arg_unit))
580 return 0;
581
582 u = unit_name_mangle(arg_unit);
583 if (!u)
584 return log_oom();
585
586 m = strappend("_SYSTEMD_UNIT=", u);
587 if (!m)
588 return log_oom();
589
590 r = sd_journal_add_match(j, m, strlen(m));
591 if (r < 0) {
592 log_error("Failed to add match: %s", strerror(-r));
593 return r;
594 }
595
596 return 0;
597}
598
941e990d
LP
599static int add_priorities(sd_journal *j) {
600 char match[] = "PRIORITY=0";
601 int i, r;
602
603 assert(j);
604
605 if (arg_priorities == 0xFF)
606 return 0;
607
608 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
609 if (arg_priorities & (1 << i)) {
610 match[sizeof(match)-2] = '0' + i;
611
941e990d
LP
612 r = sd_journal_add_match(j, match, strlen(match));
613 if (r < 0) {
614 log_error("Failed to add match: %s", strerror(-r));
615 return r;
616 }
617 }
618
619 return 0;
620}
621
7560fffc
LP
622static int setup_keys(void) {
623#ifdef HAVE_GCRYPT
624 size_t mpk_size, seed_size, state_size, i;
625 uint8_t *mpk, *seed, *state;
626 ssize_t l;
f982e6f7 627 int fd = -1, r, attr = 0;
7560fffc
LP
628 sd_id128_t machine, boot;
629 char *p = NULL, *k = NULL;
baed47c3 630 struct FSSHeader h;
14d10188 631 uint64_t n;
7560fffc
LP
632
633 r = sd_id128_get_machine(&machine);
634 if (r < 0) {
635 log_error("Failed to get machine ID: %s", strerror(-r));
636 return r;
637 }
638
639 r = sd_id128_get_boot(&boot);
640 if (r < 0) {
641 log_error("Failed to get boot ID: %s", strerror(-r));
642 return r;
643 }
644
baed47c3 645 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
646 SD_ID128_FORMAT_VAL(machine)) < 0)
647 return log_oom();
648
649 if (access(p, F_OK) >= 0) {
f7fab8a5 650 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
651 r = -EEXIST;
652 goto finish;
653 }
654
baed47c3 655 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
656 SD_ID128_FORMAT_VAL(machine)) < 0) {
657 r = log_oom();
658 goto finish;
659 }
660
661 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
662 mpk = alloca(mpk_size);
663
664 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
665 seed = alloca(seed_size);
666
667 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
668 state = alloca(state_size);
669
670 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
671 if (fd < 0) {
672 log_error("Failed to open /dev/random: %m");
673 r = -errno;
674 goto finish;
675 }
676
677 log_info("Generating seed...");
678 l = loop_read(fd, seed, seed_size, true);
679 if (l < 0 || (size_t) l != seed_size) {
680 log_error("Failed to read random seed: %s", strerror(EIO));
681 r = -EIO;
682 goto finish;
683 }
684
685 log_info("Generating key pair...");
686 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
687
baed47c3 688 log_info("Generating sealing key...");
7560fffc
LP
689 FSPRG_GenState0(state, mpk, seed, seed_size);
690
baed47c3
LP
691 assert(arg_interval > 0);
692
7560fffc 693 n = now(CLOCK_REALTIME);
baed47c3 694 n /= arg_interval;
7560fffc
LP
695
696 close_nointr_nofail(fd);
697 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
698 if (fd < 0) {
699 log_error("Failed to open %s: %m", k);
700 r = -errno;
701 goto finish;
702 }
703
f982e6f7
LP
704 /* Enable secure remove, exclusion from dump, synchronous
705 * writing and in-place updating */
706 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
707 log_warning("FS_IOC_GETFLAGS failed: %m");
708
709 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
710
711 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
712 log_warning("FS_IOC_SETFLAGS failed: %m");
713
7560fffc
LP
714 zero(h);
715 memcpy(h.signature, "KSHHRHLP", 8);
716 h.machine_id = machine;
717 h.boot_id = boot;
718 h.header_size = htole64(sizeof(h));
baed47c3
LP
719 h.start_usec = htole64(n * arg_interval);
720 h.interval_usec = htole64(arg_interval);
721 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
722 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
723
724 l = loop_write(fd, &h, sizeof(h), false);
725 if (l < 0 || (size_t) l != sizeof(h)) {
726 log_error("Failed to write header: %s", strerror(EIO));
727 r = -EIO;
728 goto finish;
729 }
730
731 l = loop_write(fd, state, state_size, false);
732 if (l < 0 || (size_t) l != state_size) {
733 log_error("Failed to write state: %s", strerror(EIO));
734 r = -EIO;
735 goto finish;
736 }
737
738 if (link(k, p) < 0) {
739 log_error("Failed to link file: %m");
740 r = -errno;
741 goto finish;
742 }
743
8481248b 744 if (on_tty()) {
7560fffc
LP
745 fprintf(stderr,
746 "\n"
baed47c3 747 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
748 "the following local file. This key file is automatically updated when the\n"
749 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
750 "\n"
751 "\t%s\n"
752 "\n"
baed47c3
LP
753 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
754 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
755 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
756 fflush(stderr);
757 }
758 for (i = 0; i < seed_size; i++) {
759 if (i > 0 && i % 3 == 0)
760 putchar('-');
761 printf("%02x", ((uint8_t*) seed)[i]);
762 }
763
baed47c3
LP
764 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
765
8481248b 766 if (on_tty()) {
f6a971bc 767 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 768
baed47c3
LP
769 fprintf(stderr,
770 ANSI_HIGHLIGHT_OFF "\n"
771 "The sealing key is automatically changed every %s.\n",
772 format_timespan(tsb, sizeof(tsb), arg_interval));
f6a971bc
LP
773
774 hn = gethostname_malloc();
775
776 if (hn) {
777 hostname_cleanup(hn);
adac1c93 778 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 779 } else
adac1c93 780 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
781
782#ifdef HAVE_QRENCODE
cf5a3432 783 /* If this is not an UTF-8 system don't print any QR codes */
09017585 784 if (is_locale_utf8()) {
cf5a3432
LP
785 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
786 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
787 }
f6a971bc
LP
788#endif
789 free(hn);
baed47c3 790 }
7560fffc
LP
791
792 r = 0;
793
794finish:
795 if (fd >= 0)
796 close_nointr_nofail(fd);
797
798 if (k) {
799 unlink(k);
800 free(k);
801 }
802
803 free(p);
804
805 return r;
806#else
feb12d3e
LP
807 log_error("Forward-secure sealing not available.");
808 return -ENOTSUP;
7560fffc
LP
809#endif
810}
811
beec0085
LP
812static int verify(sd_journal *j) {
813 int r = 0;
814 Iterator i;
815 JournalFile *f;
816
817 assert(j);
818
cedb42bb
LP
819 log_show_color(true);
820
beec0085
LP
821 HASHMAP_FOREACH(f, j->files, i) {
822 int k;
2a7b539a 823 usec_t first, validated, last;
beec0085 824
56e81f7c 825#ifdef HAVE_GCRYPT
feb12d3e 826 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 827 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 828#endif
4da416aa 829
2a7b539a 830 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 831 if (k == -EINVAL) {
baed47c3 832 /* If the key was invalid give up right-away. */
56e81f7c
LP
833 return k;
834 } else if (k < 0) {
beec0085 835 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 836 r = k;
6c7be122
LP
837 } else {
838 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 839 log_info("PASS: %s", f->path);
6c7be122 840
c0ca7aee 841 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 842 if (validated > 0) {
c0ca7aee 843 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
844 format_timestamp(a, sizeof(a), first),
845 format_timestamp(b, sizeof(b), validated),
846 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
847 } else if (last > 0)
c0ca7aee 848 log_info("=> No sealing yet, %s of entries not sealed.",
2a7b539a 849 format_timespan(c, sizeof(c), last - first));
c0ca7aee
LP
850 else
851 log_info("=> No sealing yet, no entries in file.");
852 }
6c7be122 853 }
beec0085
LP
854 }
855
856 return r;
857}
858
15804ceb
LP
859static int access_check(void) {
860
861#ifdef HAVE_ACL
862 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
863 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
864 return -EACCES;
865 }
866
867 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
868 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
869#else
870 if (geteuid() != 0 && in_group("adm") <= 0) {
871 log_error("No access to messages. Only users in the group 'adm' can see messages.");
872 return -EACCES;
873 }
874#endif
875
876 return 0;
877}
878
a963990f
LP
879int main(int argc, char *argv[]) {
880 int r;
881 sd_journal *j = NULL;
a963990f 882 bool need_seek = false;
14a65d65 883 sd_id128_t previous_boot_id;
67e04a48
ZJS
884 bool previous_boot_id_valid = false, first_line = true;
885 int n_shown = 0;
a963990f 886
a9cdc94f 887 setlocale(LC_ALL, "");
a963990f
LP
888 log_parse_environment();
889 log_open();
890
891 r = parse_argv(argc, argv);
892 if (r <= 0)
893 goto finish;
894
ed757c0c
LP
895 signal(SIGWINCH, columns_lines_cache_reset);
896
7560fffc 897 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
898 r = generate_new_id128();
899 goto finish;
900 }
901
7560fffc
LP
902 if (arg_action == ACTION_SETUP_KEYS) {
903 r = setup_keys();
904 goto finish;
905 }
906
d4205751
LP
907 if (arg_action == ACTION_LIST_CATALOG) {
908 r = catalog_list(stdout);
83f6936a
LP
909 if (r < 0)
910 log_error("Failed to list catalog: %s", strerror(-r));
d4205751
LP
911 goto finish;
912 }
913
914 if (arg_action == ACTION_UPDATE_CATALOG) {
915 r = catalog_update();
916 goto finish;
917 }
918
15804ceb
LP
919 r = access_check();
920 if (r < 0)
921 goto finish;
922
a963990f
LP
923 if (arg_directory)
924 r = sd_journal_open_directory(&j, arg_directory, 0);
925 else
9e8a535f 926 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
927 if (r < 0) {
928 log_error("Failed to open journal: %s", strerror(-r));
929 goto finish;
930 }
931
beec0085
LP
932 if (arg_action == ACTION_VERIFY) {
933 r = verify(j);
934 goto finish;
935 }
936
7560fffc 937 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e
LP
938 journal_print_header(j);
939 r = 0;
940 goto finish;
941 }
942
a1a03e30
LP
943 if (arg_action == ACTION_DISK_USAGE) {
944 uint64_t bytes;
945 char sbytes[FORMAT_BYTES_MAX];
946
947 r = sd_journal_get_usage(j, &bytes);
948 if (r < 0)
949 goto finish;
950
951 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
952 r = 0;
953 goto finish;
954 }
955
a963990f
LP
956 r = add_this_boot(j);
957 if (r < 0)
958 goto finish;
959
c3f60ec5
LP
960 r = add_unit(j);
961 if (r < 0)
962 goto finish;
963
a963990f
LP
964 r = add_matches(j, argv + optind);
965 if (r < 0)
966 goto finish;
967
941e990d
LP
968 r = add_priorities(j);
969 if (r < 0)
970 goto finish;
971
67e04a48
ZJS
972 /* Opening the fd now means the first sd_journal_wait() will actually wait */
973 r = sd_journal_get_fd(j);
974 if (r < 0)
975 goto finish;
976
15119c16
LP
977 if (arg_field) {
978 const void *data;
979 size_t size;
980
981 r = sd_journal_query_unique(j, arg_field);
982 if (r < 0) {
983 log_error("Failed to query unique data objects: %s", strerror(-r));
984 goto finish;
985 }
986
987 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
988 const void *eq;
989
67e04a48 990 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
991 break;
992
15119c16
LP
993 eq = memchr(data, '=', size);
994 if (eq)
995 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
996 else
997 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
998
999 n_shown ++;
15119c16
LP
1000 }
1001
1002 r = 0;
1003 goto finish;
1004 }
1005
cfbc22ab
LP
1006 if (arg_cursor) {
1007 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 1008 if (r < 0) {
cfbc22ab 1009 log_error("Failed to seek to cursor: %s", strerror(-r));
08984293
LP
1010 goto finish;
1011 }
d89d6c86
LN
1012 if (!arg_reverse)
1013 r = sd_journal_next(j);
1014 else
1015 r = sd_journal_previous(j);
08984293 1016
d89d6c86 1017 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1018 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1019 if (r < 0) {
cfbc22ab 1020 log_error("Failed to seek to date: %s", strerror(-r));
8f14c832
LP
1021 goto finish;
1022 }
8f14c832
LP
1023 r = sd_journal_next(j);
1024
d89d6c86
LN
1025 } else if (arg_until_set && arg_reverse) {
1026 r = sd_journal_seek_realtime_usec(j, arg_until);
1027 if (r < 0) {
1028 log_error("Failed to seek to date: %s", strerror(-r));
1029 goto finish;
1030 }
1031 r = sd_journal_previous(j);
1032
67e04a48 1033 } else if (arg_lines >= 0) {
2100675e
LP
1034 r = sd_journal_seek_tail(j);
1035 if (r < 0) {
1036 log_error("Failed to seek to tail: %s", strerror(-r));
1037 goto finish;
1038 }
1039
1040 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1041
d89d6c86
LN
1042 } else if (arg_reverse) {
1043 r = sd_journal_seek_tail(j);
1044 if (r < 0) {
1045 log_error("Failed to seek to tail: %s", strerror(-r));
1046 goto finish;
1047 }
1048
1049 r = sd_journal_previous(j);
1050
2100675e
LP
1051 } else {
1052 r = sd_journal_seek_head(j);
1053 if (r < 0) {
1054 log_error("Failed to seek to head: %s", strerror(-r));
1055 goto finish;
1056 }
6f003b43
LP
1057
1058 r = sd_journal_next(j);
1059 }
1060
1061 if (r < 0) {
1062 log_error("Failed to iterate through journal: %s", strerror(-r));
1063 goto finish;
50f20cfd 1064 }
87d2c1ff 1065
f89a3b6f
LP
1066 if (!arg_no_pager && !arg_follow)
1067 pager_open();
0d43c694 1068
cfbc22ab
LP
1069 if (!arg_quiet) {
1070 usec_t start, end;
1071 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1072
1073 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1074 if (r < 0) {
1075 log_error("Failed to get cutoff: %s", strerror(-r));
1076 goto finish;
1077 }
1078
1079 if (r > 0) {
1080 if (arg_follow)
9048b11f
LP
1081 printf("-- Logs begin at %s. --\n",
1082 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1083 else
9048b11f 1084 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1085 format_timestamp(start_buf, sizeof(start_buf), start),
1086 format_timestamp(end_buf, sizeof(end_buf), end));
1087 }
1088 }
1089
50f20cfd 1090 for (;;) {
67e04a48 1091 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1092 int flags;
1093
6f003b43 1094 if (need_seek) {
d89d6c86
LN
1095 if(!arg_reverse)
1096 r = sd_journal_next(j);
1097 else
1098 r = sd_journal_previous(j);
6f003b43
LP
1099 if (r < 0) {
1100 log_error("Failed to iterate through journal: %s", strerror(-r));
1101 goto finish;
1102 }
0d43c694
LP
1103 }
1104
1105 if (r == 0)
1106 break;
1107
d89d6c86 1108 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1109 usec_t usec;
1110
1111 r = sd_journal_get_realtime_usec(j, &usec);
1112 if (r < 0) {
1113 log_error("Failed to determine timestamp: %s", strerror(-r));
1114 goto finish;
1115 }
3ba09ee8
PF
1116 if (usec > arg_until)
1117 goto finish;
cfbc22ab
LP
1118 }
1119
d89d6c86
LN
1120 if (arg_since_set && arg_reverse) {
1121 usec_t usec;
1122
1123 r = sd_journal_get_realtime_usec(j, &usec);
1124 if (r < 0) {
1125 log_error("Failed to determine timestamp: %s", strerror(-r));
1126 goto finish;
1127 }
1128 if (usec < arg_since)
1129 goto finish;
1130 }
1131
cd931c0a
LP
1132 if (!arg_merge) {
1133 sd_id128_t boot_id;
14a65d65 1134
cd931c0a
LP
1135 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1136 if (r >= 0) {
1137 if (previous_boot_id_valid &&
1138 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 1139 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
1140
1141 previous_boot_id = boot_id;
1142 previous_boot_id_valid = true;
1143 }
14a65d65
LP
1144 }
1145
cfbc22ab 1146 flags =
cd4b13e0 1147 arg_all * OUTPUT_SHOW_ALL |
e3657ecd 1148 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
d4205751
LP
1149 on_tty() * OUTPUT_COLOR |
1150 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1151
08ace05b 1152 r = output_journal(stdout, j, arg_output, 0, flags);
244692cb 1153 if (r < 0 || ferror(stdout))
72f59706 1154 goto finish;
6f003b43
LP
1155
1156 need_seek = true;
cfbc22ab 1157 n_shown++;
87d2c1ff
LP
1158 }
1159
50f20cfd
LP
1160 if (!arg_follow)
1161 break;
1162
e02d1cf7 1163 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1164 if (r < 0) {
7a69007a 1165 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1166 goto finish;
1167 }
67e04a48
ZJS
1168
1169 first_line = false;
de190aef 1170 }
87d2c1ff
LP
1171
1172finish:
3fbf9cbb
LP
1173 if (j)
1174 sd_journal_close(j);
87d2c1ff 1175
0d43c694
LP
1176 pager_close();
1177
3fbf9cbb 1178 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1179}