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