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