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