]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
man: ensure example of sd_journal_query_unique() compiles cleanly
[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
LP
59static bool arg_follow = false;
60static bool arg_show_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
89834a7c 90 printf("%s [OPTIONS...] [MATCH]\n\n"
15119c16
LP
91 "Query the journal.\n\n"
92 "Flags:\n"
cfbc22ab
LP
93 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
94 " --since=DATE Start showing entries newer or of the specified date\n"
95 " --until=DATE Stop showing entries older or of the specified date\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"
98 " -p --priority=RANGE Show only messages within the specified priority range\n\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':
220 arg_show_all = true;
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
49826187
LP
403static bool on_tty(void) {
404 static int t = -1;
405
406 /* Note that this is invoked relatively early, before we start
407 * the pager. That means the value we return reflects whether
408 * we originally were started on a tty, not if we currently
409 * are. But this is intended, since we want colour and so on
410 * when run in our own pager. */
411
412 if (_unlikely_(t < 0))
413 t = isatty(STDOUT_FILENO) > 0;
414
415 return t;
416}
417
39f7f5c1 418static int generate_new_id128(void) {
55ee336c
LP
419 sd_id128_t id;
420 int r;
421 unsigned i;
422
423 r = sd_id128_randomize(&id);
424 if (r < 0) {
425 log_error("Failed to generate ID: %s", strerror(-r));
426 return r;
427 }
428
429 printf("As string:\n"
430 SD_ID128_FORMAT_STR "\n\n"
431 "As UUID:\n"
432 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
433 "As macro:\n"
434 "#define MESSAGE_XYZ SD_ID128_MAKE(",
435 SD_ID128_FORMAT_VAL(id),
436 SD_ID128_FORMAT_VAL(id));
437
438 for (i = 0; i < 16; i++)
439 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
440
441 fputs(")\n", stdout);
442
443 return 0;
444}
445
a963990f
LP
446static int add_matches(sd_journal *j, char **args) {
447 char **i;
448 int r;
59cea26a 449
a963990f 450 assert(j);
59cea26a 451
a963990f 452 STRV_FOREACH(i, args) {
59cea26a 453
cbdca852
LP
454 if (streq(*i, "+"))
455 r = sd_journal_add_disjunction(j);
456 else if (path_is_absolute(*i)) {
b6a34514 457 char *p, *t = NULL;
e5124088 458 const char *path;
a963990f 459 struct stat st;
e5124088 460
a963990f
LP
461 p = canonicalize_file_name(*i);
462 path = p ? p : *i;
e5124088
LP
463
464 if (stat(path, &st) < 0) {
465 free(p);
466 log_error("Couldn't stat file: %m");
a963990f 467 return -errno;
e5124088
LP
468 }
469
b6a34514 470 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 471 t = strappend("_EXE=", path);
b6a34514
LP
472 else if (S_ISCHR(st.st_mode))
473 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
474 else if (S_ISBLK(st.st_mode))
475 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
476 else {
e5124088 477 free(p);
b6a34514 478 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 479 return -EINVAL;
50940700 480 }
e5124088
LP
481
482 free(p);
b6a34514
LP
483
484 if (!t)
485 return log_oom();
486
487 r = sd_journal_add_match(j, t, 0);
488 free(t);
e5124088 489 } else
cbdca852 490 r = sd_journal_add_match(j, *i, 0);
e5124088 491
de7b95cd 492 if (r < 0) {
cbdca852 493 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 494 return r;
de7b95cd
LP
495 }
496 }
497
a963990f
LP
498 return 0;
499}
500
501static int add_this_boot(sd_journal *j) {
502 char match[9+32+1] = "_BOOT_ID=";
503 sd_id128_t boot_id;
504 int r;
505
941e990d
LP
506 assert(j);
507
a963990f
LP
508 if (!arg_this_boot)
509 return 0;
510
511 r = sd_id128_get_boot(&boot_id);
512 if (r < 0) {
513 log_error("Failed to get boot id: %s", strerror(-r));
514 return r;
515 }
516
517 sd_id128_to_string(boot_id, match + 9);
518 r = sd_journal_add_match(j, match, strlen(match));
519 if (r < 0) {
520 log_error("Failed to add match: %s", strerror(-r));
521 return r;
522 }
523
524 return 0;
525}
526
c3f60ec5
LP
527static int add_unit(sd_journal *j) {
528 _cleanup_free_ char *m = NULL, *u = NULL;
529 int r;
530
531 assert(j);
532
533 if (isempty(arg_unit))
534 return 0;
535
536 u = unit_name_mangle(arg_unit);
537 if (!u)
538 return log_oom();
539
540 m = strappend("_SYSTEMD_UNIT=", u);
541 if (!m)
542 return log_oom();
543
544 r = sd_journal_add_match(j, m, strlen(m));
545 if (r < 0) {
546 log_error("Failed to add match: %s", strerror(-r));
547 return r;
548 }
549
550 return 0;
551}
552
941e990d
LP
553static int add_priorities(sd_journal *j) {
554 char match[] = "PRIORITY=0";
555 int i, r;
556
557 assert(j);
558
559 if (arg_priorities == 0xFF)
560 return 0;
561
562 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
563 if (arg_priorities & (1 << i)) {
564 match[sizeof(match)-2] = '0' + i;
565
566 log_info("adding match %s", match);
567
568 r = sd_journal_add_match(j, match, strlen(match));
569 if (r < 0) {
570 log_error("Failed to add match: %s", strerror(-r));
571 return r;
572 }
573 }
574
575 return 0;
576}
577
7560fffc
LP
578static int setup_keys(void) {
579#ifdef HAVE_GCRYPT
580 size_t mpk_size, seed_size, state_size, i;
581 uint8_t *mpk, *seed, *state;
582 ssize_t l;
f982e6f7 583 int fd = -1, r, attr = 0;
7560fffc
LP
584 sd_id128_t machine, boot;
585 char *p = NULL, *k = NULL;
baed47c3 586 struct FSSHeader h;
14d10188 587 uint64_t n;
7560fffc
LP
588
589 r = sd_id128_get_machine(&machine);
590 if (r < 0) {
591 log_error("Failed to get machine ID: %s", strerror(-r));
592 return r;
593 }
594
595 r = sd_id128_get_boot(&boot);
596 if (r < 0) {
597 log_error("Failed to get boot ID: %s", strerror(-r));
598 return r;
599 }
600
baed47c3 601 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
602 SD_ID128_FORMAT_VAL(machine)) < 0)
603 return log_oom();
604
605 if (access(p, F_OK) >= 0) {
f7fab8a5 606 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
607 r = -EEXIST;
608 goto finish;
609 }
610
baed47c3 611 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
612 SD_ID128_FORMAT_VAL(machine)) < 0) {
613 r = log_oom();
614 goto finish;
615 }
616
617 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
618 mpk = alloca(mpk_size);
619
620 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
621 seed = alloca(seed_size);
622
623 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
624 state = alloca(state_size);
625
626 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
627 if (fd < 0) {
628 log_error("Failed to open /dev/random: %m");
629 r = -errno;
630 goto finish;
631 }
632
633 log_info("Generating seed...");
634 l = loop_read(fd, seed, seed_size, true);
635 if (l < 0 || (size_t) l != seed_size) {
636 log_error("Failed to read random seed: %s", strerror(EIO));
637 r = -EIO;
638 goto finish;
639 }
640
641 log_info("Generating key pair...");
642 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
643
baed47c3 644 log_info("Generating sealing key...");
7560fffc
LP
645 FSPRG_GenState0(state, mpk, seed, seed_size);
646
baed47c3
LP
647 assert(arg_interval > 0);
648
7560fffc 649 n = now(CLOCK_REALTIME);
baed47c3 650 n /= arg_interval;
7560fffc
LP
651
652 close_nointr_nofail(fd);
653 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
654 if (fd < 0) {
655 log_error("Failed to open %s: %m", k);
656 r = -errno;
657 goto finish;
658 }
659
f982e6f7
LP
660 /* Enable secure remove, exclusion from dump, synchronous
661 * writing and in-place updating */
662 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
663 log_warning("FS_IOC_GETFLAGS failed: %m");
664
665 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
666
667 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
668 log_warning("FS_IOC_SETFLAGS failed: %m");
669
7560fffc
LP
670 zero(h);
671 memcpy(h.signature, "KSHHRHLP", 8);
672 h.machine_id = machine;
673 h.boot_id = boot;
674 h.header_size = htole64(sizeof(h));
baed47c3
LP
675 h.start_usec = htole64(n * arg_interval);
676 h.interval_usec = htole64(arg_interval);
677 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
678 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
679
680 l = loop_write(fd, &h, sizeof(h), false);
681 if (l < 0 || (size_t) l != sizeof(h)) {
682 log_error("Failed to write header: %s", strerror(EIO));
683 r = -EIO;
684 goto finish;
685 }
686
687 l = loop_write(fd, state, state_size, false);
688 if (l < 0 || (size_t) l != state_size) {
689 log_error("Failed to write state: %s", strerror(EIO));
690 r = -EIO;
691 goto finish;
692 }
693
694 if (link(k, p) < 0) {
695 log_error("Failed to link file: %m");
696 r = -errno;
697 goto finish;
698 }
699
700 if (isatty(STDOUT_FILENO)) {
701 fprintf(stderr,
702 "\n"
baed47c3 703 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
704 "the following local file. This key file is automatically updated when the\n"
705 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
706 "\n"
707 "\t%s\n"
708 "\n"
baed47c3
LP
709 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
710 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
711 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
712 fflush(stderr);
713 }
714 for (i = 0; i < seed_size; i++) {
715 if (i > 0 && i % 3 == 0)
716 putchar('-');
717 printf("%02x", ((uint8_t*) seed)[i]);
718 }
719
baed47c3
LP
720 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
721
722 if (isatty(STDOUT_FILENO)) {
f6a971bc 723 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 724
baed47c3
LP
725 fprintf(stderr,
726 ANSI_HIGHLIGHT_OFF "\n"
727 "The sealing key is automatically changed every %s.\n",
728 format_timespan(tsb, sizeof(tsb), arg_interval));
f6a971bc
LP
729
730 hn = gethostname_malloc();
731
732 if (hn) {
733 hostname_cleanup(hn);
adac1c93 734 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 735 } else
adac1c93 736 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
737
738#ifdef HAVE_QRENCODE
cf5a3432
LP
739 /* If this is not an UTF-8 system don't print any QR codes */
740 setlocale(LC_CTYPE, "");
741
742 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
743 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
744 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
745 }
f6a971bc
LP
746#endif
747 free(hn);
baed47c3 748 }
7560fffc
LP
749
750 r = 0;
751
752finish:
753 if (fd >= 0)
754 close_nointr_nofail(fd);
755
756 if (k) {
757 unlink(k);
758 free(k);
759 }
760
761 free(p);
762
763 return r;
764#else
feb12d3e
LP
765 log_error("Forward-secure sealing not available.");
766 return -ENOTSUP;
7560fffc
LP
767#endif
768}
769
beec0085
LP
770static int verify(sd_journal *j) {
771 int r = 0;
772 Iterator i;
773 JournalFile *f;
774
775 assert(j);
776
cedb42bb
LP
777 log_show_color(true);
778
beec0085
LP
779 HASHMAP_FOREACH(f, j->files, i) {
780 int k;
2a7b539a 781 usec_t first, validated, last;
beec0085 782
56e81f7c 783#ifdef HAVE_GCRYPT
feb12d3e 784 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 785 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 786#endif
4da416aa 787
2a7b539a 788 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 789 if (k == -EINVAL) {
baed47c3 790 /* If the key was invalid give up right-away. */
56e81f7c
LP
791 return k;
792 } else if (k < 0) {
beec0085 793 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 794 r = k;
6c7be122
LP
795 } else {
796 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 797 log_info("PASS: %s", f->path);
6c7be122 798
c0ca7aee 799 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 800 if (validated > 0) {
c0ca7aee 801 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
802 format_timestamp(a, sizeof(a), first),
803 format_timestamp(b, sizeof(b), validated),
804 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
805 } else if (last > 0)
c0ca7aee 806 log_info("=> No sealing yet, %s of entries not sealed.",
2a7b539a 807 format_timespan(c, sizeof(c), last - first));
c0ca7aee
LP
808 else
809 log_info("=> No sealing yet, no entries in file.");
810 }
6c7be122 811 }
beec0085
LP
812 }
813
814 return r;
815}
816
15804ceb
LP
817static int access_check(void) {
818
819#ifdef HAVE_ACL
820 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
821 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
822 return -EACCES;
823 }
824
825 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
826 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
827#else
828 if (geteuid() != 0 && in_group("adm") <= 0) {
829 log_error("No access to messages. Only users in the group 'adm' can see messages.");
830 return -EACCES;
831 }
832#endif
833
834 return 0;
835}
836
a963990f
LP
837int main(int argc, char *argv[]) {
838 int r;
839 sd_journal *j = NULL;
a963990f 840 bool need_seek = false;
14a65d65
LP
841 sd_id128_t previous_boot_id;
842 bool previous_boot_id_valid = false;
92a1fd9e 843 bool have_pager;
cfbc22ab 844 unsigned n_shown = 0;
a963990f
LP
845
846 log_parse_environment();
847 log_open();
848
849 r = parse_argv(argc, argv);
850 if (r <= 0)
851 goto finish;
852
7560fffc 853 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
854 r = generate_new_id128();
855 goto finish;
856 }
857
7560fffc
LP
858 if (arg_action == ACTION_SETUP_KEYS) {
859 r = setup_keys();
860 goto finish;
861 }
862
15804ceb
LP
863 r = access_check();
864 if (r < 0)
865 goto finish;
866
a963990f
LP
867 if (arg_directory)
868 r = sd_journal_open_directory(&j, arg_directory, 0);
869 else
9e8a535f 870 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
871 if (r < 0) {
872 log_error("Failed to open journal: %s", strerror(-r));
873 goto finish;
874 }
875
beec0085
LP
876 if (arg_action == ACTION_VERIFY) {
877 r = verify(j);
878 goto finish;
879 }
880
7560fffc 881 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e
LP
882 journal_print_header(j);
883 r = 0;
884 goto finish;
885 }
886
a1a03e30
LP
887 if (arg_action == ACTION_DISK_USAGE) {
888 uint64_t bytes;
889 char sbytes[FORMAT_BYTES_MAX];
890
891 r = sd_journal_get_usage(j, &bytes);
892 if (r < 0)
893 goto finish;
894
895 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
896 r = 0;
897 goto finish;
898 }
899
a963990f
LP
900 r = add_this_boot(j);
901 if (r < 0)
902 goto finish;
903
c3f60ec5
LP
904 r = add_unit(j);
905 if (r < 0)
906 goto finish;
907
a963990f
LP
908 r = add_matches(j, argv + optind);
909 if (r < 0)
910 goto finish;
911
941e990d
LP
912 r = add_priorities(j);
913 if (r < 0)
914 goto finish;
915
15119c16
LP
916 if (arg_field) {
917 const void *data;
918 size_t size;
919
920 r = sd_journal_query_unique(j, arg_field);
921 if (r < 0) {
922 log_error("Failed to query unique data objects: %s", strerror(-r));
923 goto finish;
924 }
925
926 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
927 const void *eq;
928
929 eq = memchr(data, '=', size);
930 if (eq)
931 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
932 else
933 printf("%.*s\n", (int) size, (const char*) data);
934 }
935
936 r = 0;
937 goto finish;
938 }
939
cfbc22ab
LP
940 if (arg_cursor) {
941 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 942 if (r < 0) {
cfbc22ab 943 log_error("Failed to seek to cursor: %s", strerror(-r));
08984293
LP
944 goto finish;
945 }
946
cfbc22ab 947 r = sd_journal_next(j);
08984293 948
cfbc22ab
LP
949 } else if (arg_since_set) {
950 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 951 if (r < 0) {
cfbc22ab 952 log_error("Failed to seek to date: %s", strerror(-r));
8f14c832
LP
953 goto finish;
954 }
8f14c832
LP
955 r = sd_journal_next(j);
956
cfbc22ab 957 } else if (arg_lines > 0) {
2100675e
LP
958 r = sd_journal_seek_tail(j);
959 if (r < 0) {
960 log_error("Failed to seek to tail: %s", strerror(-r));
961 goto finish;
962 }
963
964 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 965
2100675e
LP
966 } else {
967 r = sd_journal_seek_head(j);
968 if (r < 0) {
969 log_error("Failed to seek to head: %s", strerror(-r));
970 goto finish;
971 }
6f003b43
LP
972
973 r = sd_journal_next(j);
974 }
975
976 if (r < 0) {
977 log_error("Failed to iterate through journal: %s", strerror(-r));
978 goto finish;
50f20cfd 979 }
87d2c1ff 980
49826187 981 on_tty();
fafb6ecc 982 have_pager = !arg_no_pager && !arg_follow && pager_open();
0d43c694 983
cfbc22ab
LP
984 if (!arg_quiet) {
985 usec_t start, end;
986 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
987
988 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
989 if (r < 0) {
990 log_error("Failed to get cutoff: %s", strerror(-r));
991 goto finish;
992 }
993
994 if (r > 0) {
995 if (arg_follow)
9048b11f
LP
996 printf("-- Logs begin at %s. --\n",
997 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 998 else
9048b11f 999 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1000 format_timestamp(start_buf, sizeof(start_buf), start),
1001 format_timestamp(end_buf, sizeof(end_buf), end));
1002 }
1003 }
1004
50f20cfd 1005 for (;;) {
c3eba2ab 1006 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
cfbc22ab
LP
1007 int flags;
1008
6f003b43
LP
1009 if (need_seek) {
1010 r = sd_journal_next(j);
1011 if (r < 0) {
1012 log_error("Failed to iterate through journal: %s", strerror(-r));
1013 goto finish;
1014 }
0d43c694
LP
1015 }
1016
1017 if (r == 0)
1018 break;
1019
cfbc22ab
LP
1020 if (arg_until_set) {
1021 usec_t usec;
1022
1023 r = sd_journal_get_realtime_usec(j, &usec);
1024 if (r < 0) {
1025 log_error("Failed to determine timestamp: %s", strerror(-r));
1026 goto finish;
1027 }
1028 }
1029
cd931c0a
LP
1030 if (!arg_merge) {
1031 sd_id128_t boot_id;
14a65d65 1032
cd931c0a
LP
1033 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1034 if (r >= 0) {
1035 if (previous_boot_id_valid &&
1036 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 1037 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
1038
1039 previous_boot_id = boot_id;
1040 previous_boot_id_valid = true;
1041 }
14a65d65
LP
1042 }
1043
cfbc22ab
LP
1044 flags =
1045 arg_show_all * OUTPUT_SHOW_ALL |
1046 have_pager * OUTPUT_FULL_WIDTH |
1047 on_tty() * OUTPUT_COLOR;
1048
08ace05b 1049 r = output_journal(stdout, j, arg_output, 0, flags);
72f59706
LP
1050 if (r < 0)
1051 goto finish;
6f003b43
LP
1052
1053 need_seek = true;
cfbc22ab 1054 n_shown++;
87d2c1ff
LP
1055 }
1056
50f20cfd
LP
1057 if (!arg_follow)
1058 break;
1059
e02d1cf7 1060 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1061 if (r < 0) {
7a69007a 1062 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1063 goto finish;
1064 }
de190aef 1065 }
87d2c1ff
LP
1066
1067finish:
3fbf9cbb
LP
1068 if (j)
1069 sd_journal_close(j);
87d2c1ff 1070
0d43c694
LP
1071 pager_close();
1072
3fbf9cbb 1073 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1074}