]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
Rename F_TYPE_CMP() to F_TYPE_EQUAL()
[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>
72f59706 30#include <time.h>
0d43c694 31#include <getopt.h>
585314e8 32#include <signal.h>
50940700 33#include <sys/stat.h>
f982e6f7
LP
34#include <sys/ioctl.h>
35#include <linux/fs.h>
87d2c1ff 36
4468addc
LP
37#ifdef HAVE_ACL
38#include <sys/acl.h>
478c8269 39#include "acl-util.h"
4468addc
LP
40#endif
41
81527be1
LP
42#include <systemd/sd-journal.h>
43
3fbf9cbb 44#include "log.h"
7199aa96 45#include "logs-show.h"
72f59706 46#include "util.h"
e5124088 47#include "path-util.h"
68fee104 48#include "fileio.h"
0d43c694
LP
49#include "build.h"
50#include "pager.h"
86aa7ba4 51#include "logs-show.h"
a963990f 52#include "strv.h"
dca6219e 53#include "journal-internal.h"
7560fffc 54#include "journal-def.h"
0284adc6 55#include "journal-verify.h"
4da416aa 56#include "journal-authenticate.h"
f6a971bc 57#include "journal-qrcode.h"
4da416aa 58#include "fsprg.h"
c3f60ec5 59#include "unit-name.h"
d4205751 60#include "catalog.h"
7560fffc 61
baed47c3 62#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 63
df50185b 64static OutputMode arg_output = OUTPUT_SHORT;
1b12a7b5 65static bool arg_pager_end = false;
72f59706 66static bool arg_follow = false;
e3657ecd 67static bool arg_full = false;
cd4b13e0 68static bool arg_all = false;
0d43c694 69static bool arg_no_pager = false;
67e04a48 70static int arg_lines = -1;
e91af489 71static bool arg_no_tail = false;
43673799 72static bool arg_quiet = false;
9e8a535f 73static bool arg_merge = false;
d121b396
ZJS
74static bool arg_boot = false;
75static char *arg_boot_descriptor = NULL;
99271804 76static bool arg_dmesg = false;
8f14c832 77static const char *arg_cursor = NULL;
248fc619
ZJS
78static const char *arg_after_cursor = NULL;
79static bool arg_show_cursor = false;
a963990f 80static const char *arg_directory = NULL;
8d98da3f 81static char **arg_file = NULL;
941e990d 82static int arg_priorities = 0xFF;
baed47c3 83static const char *arg_verify_key = NULL;
feb12d3e 84#ifdef HAVE_GCRYPT
baed47c3 85static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
b8547c10 86static bool arg_force = false;
feb12d3e 87#endif
cfbc22ab
LP
88static usec_t arg_since, arg_until;
89static bool arg_since_set = false, arg_until_set = false;
b9e40524
HH
90static char **arg_system_units = NULL;
91static char **arg_user_units = NULL;
3c1668da 92static const char *arg_field = NULL;
d4205751 93static bool arg_catalog = false;
d89d6c86 94static bool arg_reverse = false;
3f3a438f 95static int arg_journal_type = 0;
13cbf3a5 96static const char *arg_root = NULL;
50f20cfd 97
7560fffc
LP
98static enum {
99 ACTION_SHOW,
100 ACTION_NEW_ID128,
101 ACTION_PRINT_HEADER,
beec0085 102 ACTION_SETUP_KEYS,
a1a03e30
LP
103 ACTION_VERIFY,
104 ACTION_DISK_USAGE,
d4205751 105 ACTION_LIST_CATALOG,
54b7254c 106 ACTION_DUMP_CATALOG,
d4205751 107 ACTION_UPDATE_CATALOG
7560fffc
LP
108} arg_action = ACTION_SHOW;
109
a331b5e6
JJ
110typedef struct boot_id_t {
111 sd_id128_t id;
112 uint64_t timestamp;
113} boot_id_t;
114
0d43c694
LP
115static int help(void) {
116
cd4b13e0 117 printf("%s [OPTIONS...] [MATCHES...]\n\n"
15119c16
LP
118 "Query the journal.\n\n"
119 "Flags:\n"
248fc619
ZJS
120 " --system Show only the system journal\n"
121 " --user Show only the user journal for current user\n"
122 " --since=DATE Start showing entries newer or of the specified date\n"
123 " --until=DATE Stop showing entries older or of the specified date\n"
124 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
125 " --after-cursor=CURSOR Start showing entries from specified cursor\n"
126 " --show-cursor Print the cursor after all the entries\n"
127 " -b --boot[=ID] Show data only from ID or current boot if unspecified\n"
128 " -k --dmesg Show kernel message log from current boot\n"
129 " -u --unit=UNIT Show data only from the specified unit\n"
130 " --user-unit=UNIT Show data only from the specified user session unit\n"
131 " -p --priority=RANGE Show only messages within the specified priority range\n"
132 " -e --pager-end Immediately jump to end of the journal in the pager\n"
133 " -f --follow Follow journal\n"
134 " -n --lines[=INTEGER] Number of journal entries to show\n"
135 " --no-tail Show all lines, even in follow mode\n"
136 " -r --reverse Show the newest entries first\n"
44bc6e1f 137 " -o --output=STRING Change journal output mode (short, short-monotonic, short-iso\n"
248fc619
ZJS
138 " verbose, export, json, json-pretty, json-sse, cat)\n"
139 " -x --catalog Add message explanations where available\n"
140 " -l --full Do not ellipsize fields\n"
141 " -a --all Show all fields, including long and unprintable\n"
142 " -q --quiet Don't show privilege warning\n"
143 " --no-pager Do not pipe output into a pager\n"
144 " -m --merge Show entries from all available journals\n"
145 " -D --directory=PATH Show journal files from directory\n"
146 " --file=PATH Show journal file\n"
147 " --root=ROOT Operate on catalog files underneath the root ROOT\n"
15119c16 148#ifdef HAVE_GCRYPT
248fc619
ZJS
149 " --interval=TIME Time interval for changing the FSS sealing key\n"
150 " --verify-key=KEY Specify FSS verification key\n"
151 " --force Force overriding new FSS key pair with --setup-keys\n"
15119c16
LP
152#endif
153 "\nCommands:\n"
248fc619
ZJS
154 " -h --help Show this help\n"
155 " --version Show package version\n"
156 " --new-id128 Generate a new 128 Bit ID\n"
157 " --header Show journal header information\n"
158 " --disk-usage Show total disk usage\n"
159 " -F --field=FIELD List all values a certain field takes\n"
160 " --list-catalog Show message IDs of all entries in the message catalog\n"
161 " --dump-catalog Show entries in the message catalog\n"
162 " --update-catalog Update the message catalog database\n"
feb12d3e 163#ifdef HAVE_GCRYPT
248fc619
ZJS
164 " --setup-keys Generate new FSS key pair\n"
165 " --verify Verify journal file consistency\n"
feb12d3e
LP
166#endif
167 , program_invocation_short_name);
0d43c694
LP
168
169 return 0;
170}
171
172static int parse_argv(int argc, char *argv[]) {
173
174 enum {
175 ARG_VERSION = 0x100,
e91af489 176 ARG_NO_PAGER,
55ee336c 177 ARG_NO_TAIL,
dca6219e 178 ARG_NEW_ID128,
3f3a438f
ZJS
179 ARG_USER,
180 ARG_SYSTEM,
13cbf3a5 181 ARG_ROOT,
7560fffc 182 ARG_HEADER,
beec0085 183 ARG_SETUP_KEYS,
8d98da3f 184 ARG_FILE,
baed47c3 185 ARG_INTERVAL,
4da416aa 186 ARG_VERIFY,
a1a03e30 187 ARG_VERIFY_KEY,
cfbc22ab
LP
188 ARG_DISK_USAGE,
189 ARG_SINCE,
d4205751 190 ARG_UNTIL,
248fc619
ZJS
191 ARG_AFTER_CURSOR,
192 ARG_SHOW_CURSOR,
ffa7cd15 193 ARG_USER_UNIT,
d4205751 194 ARG_LIST_CATALOG,
54b7254c 195 ARG_DUMP_CATALOG,
3f3a438f 196 ARG_UPDATE_CATALOG,
b8547c10 197 ARG_FORCE,
0d43c694
LP
198 };
199
200 static const struct option options[] = {
248fc619
ZJS
201 { "help", no_argument, NULL, 'h' },
202 { "version" , no_argument, NULL, ARG_VERSION },
203 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
204 { "pager-end", no_argument, NULL, 'e' },
205 { "follow", no_argument, NULL, 'f' },
206 { "force", no_argument, NULL, ARG_FORCE },
207 { "output", required_argument, NULL, 'o' },
208 { "all", no_argument, NULL, 'a' },
209 { "full", no_argument, NULL, 'l' },
210 { "lines", optional_argument, NULL, 'n' },
211 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
212 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
213 { "quiet", no_argument, NULL, 'q' },
214 { "merge", no_argument, NULL, 'm' },
215 { "boot", optional_argument, NULL, 'b' },
216 { "this-boot", optional_argument, NULL, 'b' }, /* deprecated */
217 { "dmesg", no_argument, NULL, 'k' },
218 { "system", no_argument, NULL, ARG_SYSTEM },
219 { "user", no_argument, NULL, ARG_USER },
220 { "directory", required_argument, NULL, 'D' },
221 { "file", required_argument, NULL, ARG_FILE },
222 { "root", required_argument, NULL, ARG_ROOT },
223 { "header", no_argument, NULL, ARG_HEADER },
224 { "priority", required_argument, NULL, 'p' },
225 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
226 { "interval", required_argument, NULL, ARG_INTERVAL },
227 { "verify", no_argument, NULL, ARG_VERIFY },
228 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
229 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
230 { "cursor", required_argument, NULL, 'c' },
231 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
232 { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
233 { "since", required_argument, NULL, ARG_SINCE },
234 { "until", required_argument, NULL, ARG_UNTIL },
235 { "unit", required_argument, NULL, 'u' },
236 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
237 { "field", required_argument, NULL, 'F' },
238 { "catalog", no_argument, NULL, 'x' },
239 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
240 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
241 { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
242 { "reverse", no_argument, NULL, 'r' },
243 { NULL, 0, NULL, 0 }
0d43c694
LP
244 };
245
2100675e 246 int c, r;
0d43c694
LP
247
248 assert(argc >= 0);
249 assert(argv);
250
a331b5e6 251 while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xr", options, NULL)) >= 0) {
0d43c694
LP
252
253 switch (c) {
254
255 case 'h':
256 help();
257 return 0;
258
259 case ARG_VERSION:
260 puts(PACKAGE_STRING);
0d43c694
LP
261 puts(SYSTEMD_FEATURES);
262 return 0;
263
264 case ARG_NO_PAGER:
265 arg_no_pager = true;
266 break;
267
1b12a7b5
HH
268 case 'e':
269 arg_pager_end = true;
fe59e38b
LP
270
271 if (arg_lines < 0)
272 arg_lines = 1000;
273
1b12a7b5
HH
274 break;
275
0d43c694
LP
276 case 'f':
277 arg_follow = true;
278 break;
279
280 case 'o':
1705594f 281 arg_output = output_mode_from_string(optarg);
df50185b 282 if (arg_output < 0) {
edfb521a 283 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
284 return -EINVAL;
285 }
df50185b 286
edfb521a
ZJS
287 if (arg_output == OUTPUT_EXPORT ||
288 arg_output == OUTPUT_JSON ||
289 arg_output == OUTPUT_JSON_PRETTY ||
290 arg_output == OUTPUT_JSON_SSE ||
291 arg_output == OUTPUT_CAT)
292 arg_quiet = true;
293
0d43c694
LP
294 break;
295
98a6e132 296 case 'l':
e3657ecd
ZJS
297 arg_full = true;
298 break;
299
0d43c694 300 case 'a':
cd4b13e0 301 arg_all = true;
0d43c694
LP
302 break;
303
2100675e 304 case 'n':
1705594f 305 if (optarg) {
67e04a48
ZJS
306 r = safe_atoi(optarg, &arg_lines);
307 if (r < 0 || arg_lines < 0) {
1705594f
LP
308 log_error("Failed to parse lines '%s'", optarg);
309 return -EINVAL;
310 }
96088db0
LP
311 } else {
312 int n;
313
314 /* Hmm, no argument? Maybe the next
315 * word on the command line is
316 * supposed to be the argument? Let's
317 * see if there is one, and is
318 * parsable as a positive
319 * integer... */
320
321 if (optind < argc &&
322 safe_atoi(argv[optind], &n) >= 0 &&
323 n >= 0) {
324
325 arg_lines = n;
326 optind++;
327 } else
328 arg_lines = 10;
329 }
1705594f 330
2100675e
LP
331 break;
332
e91af489
LP
333 case ARG_NO_TAIL:
334 arg_no_tail = true;
335 break;
336
39f7f5c1 337 case ARG_NEW_ID128:
7560fffc 338 arg_action = ACTION_NEW_ID128;
55ee336c
LP
339 break;
340
43673799
LP
341 case 'q':
342 arg_quiet = true;
490e567d 343 break;
43673799 344
9e8a535f
LP
345 case 'm':
346 arg_merge = true;
2bd3c38a
LP
347 break;
348
59cea26a 349 case 'b':
d121b396
ZJS
350 arg_boot = true;
351
a331b5e6 352 if (optarg)
d121b396
ZJS
353 arg_boot_descriptor = optarg;
354 else if (optind < argc) {
355 int boot;
356
357 if (argv[optind][0] != '-' ||
358 safe_atoi(argv[optind], &boot) >= 0) {
359 arg_boot_descriptor = argv[optind];
360 optind++;
361 }
a331b5e6 362 }
d121b396 363
59cea26a
LP
364 break;
365
99271804 366 case 'k':
d121b396 367 arg_boot = arg_dmesg = true;
99271804
ZJS
368 break;
369
3f3a438f
ZJS
370 case ARG_SYSTEM:
371 arg_journal_type |= SD_JOURNAL_SYSTEM;
372 break;
373
374 case ARG_USER:
375 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
376 break;
377
a963990f
LP
378 case 'D':
379 arg_directory = optarg;
380 break;
381
8d98da3f
ZJS
382 case ARG_FILE:
383 r = glob_extend(&arg_file, optarg);
384 if (r < 0) {
385 log_error("Failed to add paths: %s", strerror(-r));
386 return r;
387 };
388 break;
389
13cbf3a5
ZJS
390 case ARG_ROOT:
391 arg_root = optarg;
392 break;
393
8f14c832
LP
394 case 'c':
395 arg_cursor = optarg;
396 break;
397
248fc619
ZJS
398 case ARG_AFTER_CURSOR:
399 arg_after_cursor = optarg;
400 break;
401
402 case ARG_SHOW_CURSOR:
403 arg_show_cursor = true;
404 break;
405
dca6219e 406 case ARG_HEADER:
7560fffc
LP
407 arg_action = ACTION_PRINT_HEADER;
408 break;
409
feb12d3e
LP
410 case ARG_VERIFY:
411 arg_action = ACTION_VERIFY;
412 break;
413
a1a03e30
LP
414 case ARG_DISK_USAGE:
415 arg_action = ACTION_DISK_USAGE;
416 break;
417
feb12d3e 418#ifdef HAVE_GCRYPT
b8547c10
SL
419 case ARG_FORCE:
420 arg_force = true;
421 break;
422
7560fffc
LP
423 case ARG_SETUP_KEYS:
424 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
425 break;
426
beec0085 427
baed47c3 428 case ARG_VERIFY_KEY:
4da416aa 429 arg_action = ACTION_VERIFY;
baed47c3 430 arg_verify_key = optarg;
9e8a535f 431 arg_merge = false;
4da416aa
LP
432 break;
433
baed47c3 434 case ARG_INTERVAL:
7f602784 435 r = parse_sec(optarg, &arg_interval);
baed47c3
LP
436 if (r < 0 || arg_interval <= 0) {
437 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
438 return -EINVAL;
439 }
440 break;
feb12d3e
LP
441#else
442 case ARG_SETUP_KEYS:
443 case ARG_VERIFY_KEY:
444 case ARG_INTERVAL:
b8547c10 445 case ARG_FORCE:
feb12d3e
LP
446 log_error("Forward-secure sealing not available.");
447 return -ENOTSUP;
448#endif
14d10188 449
941e990d
LP
450 case 'p': {
451 const char *dots;
452
453 dots = strstr(optarg, "..");
454 if (dots) {
455 char *a;
456 int from, to, i;
457
458 /* a range */
459 a = strndup(optarg, dots - optarg);
460 if (!a)
461 return log_oom();
462
463 from = log_level_from_string(a);
464 to = log_level_from_string(dots + 2);
465 free(a);
466
467 if (from < 0 || to < 0) {
468 log_error("Failed to parse log level range %s", optarg);
469 return -EINVAL;
470 }
471
472 arg_priorities = 0;
473
474 if (from < to) {
475 for (i = from; i <= to; i++)
476 arg_priorities |= 1 << i;
477 } else {
478 for (i = to; i <= from; i++)
479 arg_priorities |= 1 << i;
480 }
481
482 } else {
483 int p, i;
484
485 p = log_level_from_string(optarg);
486 if (p < 0) {
487 log_error("Unknown log level %s", optarg);
488 return -EINVAL;
489 }
490
491 arg_priorities = 0;
492
493 for (i = 0; i <= p; i++)
494 arg_priorities |= 1 << i;
495 }
496
497 break;
498 }
499
cfbc22ab
LP
500 case ARG_SINCE:
501 r = parse_timestamp(optarg, &arg_since);
502 if (r < 0) {
503 log_error("Failed to parse timestamp: %s", optarg);
504 return -EINVAL;
505 }
506 arg_since_set = true;
507 break;
508
509 case ARG_UNTIL:
510 r = parse_timestamp(optarg, &arg_until);
511 if (r < 0) {
512 log_error("Failed to parse timestamp: %s", optarg);
513 return -EINVAL;
514 }
515 arg_until_set = true;
516 break;
517
7199aa96 518 case 'u':
b9e40524
HH
519 r = strv_extend(&arg_system_units, optarg);
520 if (r < 0)
521 return log_oom();
ffa7cd15
DW
522 break;
523
7199aa96 524 case ARG_USER_UNIT:
b9e40524
HH
525 r = strv_extend(&arg_user_units, optarg);
526 if (r < 0)
527 return log_oom();
c3f60ec5
LP
528 break;
529
0d43c694
LP
530 case '?':
531 return -EINVAL;
532
15119c16
LP
533 case 'F':
534 arg_field = optarg;
535 break;
536
d4205751
LP
537 case 'x':
538 arg_catalog = true;
539 break;
540
541 case ARG_LIST_CATALOG:
542 arg_action = ACTION_LIST_CATALOG;
543 break;
544
54b7254c
ZJS
545 case ARG_DUMP_CATALOG:
546 arg_action = ACTION_DUMP_CATALOG;
547 break;
548
d4205751
LP
549 case ARG_UPDATE_CATALOG:
550 arg_action = ACTION_UPDATE_CATALOG;
551 break;
552
d89d6c86
LN
553 case 'r':
554 arg_reverse = true;
555 break;
556
0d43c694
LP
557 default:
558 log_error("Unknown option code %c", c);
559 return -EINVAL;
560 }
561 }
562
67e04a48 563 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
564 arg_lines = 10;
565
8d98da3f
ZJS
566 if (arg_directory && arg_file) {
567 log_error("Please specify either -D/--directory= or --file=, not both.");
568 return -EINVAL;
569 }
570
3ba09ee8 571 if (arg_since_set && arg_until_set && arg_since > arg_until) {
cfbc22ab
LP
572 log_error("--since= must be before --until=.");
573 return -EINVAL;
574 }
575
248fc619
ZJS
576 if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) {
577 log_error("Please specify only one of --since=, --cursor=, and --after-cursor.");
cfbc22ab
LP
578 return -EINVAL;
579 }
580
d89d6c86
LN
581 if (arg_follow && arg_reverse) {
582 log_error("Please specify either --reverse= or --follow=, not both.");
583 return -EINVAL;
584 }
585
0d43c694
LP
586 return 1;
587}
588
39f7f5c1 589static int generate_new_id128(void) {
55ee336c
LP
590 sd_id128_t id;
591 int r;
592 unsigned i;
593
594 r = sd_id128_randomize(&id);
595 if (r < 0) {
596 log_error("Failed to generate ID: %s", strerror(-r));
597 return r;
598 }
599
600 printf("As string:\n"
601 SD_ID128_FORMAT_STR "\n\n"
602 "As UUID:\n"
603 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
604 "As macro:\n"
d489071f 605 "#define MESSAGE_XYZ SD_ID128_MAKE(",
55ee336c
LP
606 SD_ID128_FORMAT_VAL(id),
607 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
608 for (i = 0; i < 16; i++)
609 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
d489071f 610 fputs(")\n\n", stdout);
55ee336c 611
d489071f
ZJS
612 printf("As Python constant:\n"
613 ">>> import uuid\n"
614 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
615 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
616
617 return 0;
618}
619
a963990f
LP
620static int add_matches(sd_journal *j, char **args) {
621 char **i;
59cea26a 622
a963990f 623 assert(j);
59cea26a 624
a963990f 625 STRV_FOREACH(i, args) {
52aeb63c 626 int r;
59cea26a 627
cbdca852
LP
628 if (streq(*i, "+"))
629 r = sd_journal_add_disjunction(j);
630 else if (path_is_absolute(*i)) {
68fee104 631 _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
e5124088 632 const char *path;
68fee104 633 _cleanup_free_ char *interpreter = NULL;
a963990f 634 struct stat st;
e5124088 635
a963990f
LP
636 p = canonicalize_file_name(*i);
637 path = p ? p : *i;
e5124088
LP
638
639 if (stat(path, &st) < 0) {
e5124088 640 log_error("Couldn't stat file: %m");
a963990f 641 return -errno;
e5124088
LP
642 }
643
68fee104
ZJS
644 if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
645 if (executable_is_script(path, &interpreter) > 0) {
646 _cleanup_free_ char *comm;
647
648 comm = strndup(path_get_file_name(path), 15);
649 if (!comm)
650 return log_oom();
651
652 t = strappend("_COMM=", comm);
653
654 /* Append _EXE only if the interpreter is not a link.
655 Otherwise it might be outdated often. */
656 if (lstat(interpreter, &st) == 0 &&
657 !S_ISLNK(st.st_mode)) {
658 t2 = strappend("_EXE=", interpreter);
659 if (!t2)
660 return log_oom();
661 }
662 } else
663 t = strappend("_EXE=", path);
664 } else if (S_ISCHR(st.st_mode))
b6a34514
LP
665 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
666 else if (S_ISBLK(st.st_mode))
667 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
668 else {
fb93cf73 669 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
a963990f 670 return -EINVAL;
50940700 671 }
e5124088 672
b6a34514
LP
673 if (!t)
674 return log_oom();
675
676 r = sd_journal_add_match(j, t, 0);
68fee104
ZJS
677 if (t2)
678 r = sd_journal_add_match(j, t2, 0);
e5124088 679 } else
cbdca852 680 r = sd_journal_add_match(j, *i, 0);
e5124088 681
de7b95cd 682 if (r < 0) {
cbdca852 683 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 684 return r;
de7b95cd
LP
685 }
686 }
687
a963990f
LP
688 return 0;
689}
690
a331b5e6
JJ
691static int boot_id_cmp(const void *a, const void *b) {
692 uint64_t _a, _b;
693
694 _a = ((const boot_id_t *)a)->timestamp;
695 _b = ((const boot_id_t *)b)->timestamp;
696
697 return _a < _b ? -1 : (_a > _b ? 1 : 0);
698}
699
700static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) {
701 int r;
702 const void *data;
d121b396 703 unsigned int count = 0;
a331b5e6 704 size_t length, allocated = 0;
d121b396 705 boot_id_t ref_boot_id = {SD_ID128_NULL}, *id;
a331b5e6 706 _cleanup_free_ boot_id_t *all_ids = NULL;
a331b5e6
JJ
707
708 assert(j);
709 assert(boot_id);
710
d121b396 711 if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
a331b5e6
JJ
712 return 0;
713
a331b5e6
JJ
714 r = sd_journal_query_unique(j, "_BOOT_ID");
715 if (r < 0)
716 return r;
717
718 SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
719 if (length < strlen("_BOOT_ID="))
720 continue;
721
d121b396 722 if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
a331b5e6
JJ
723 return log_oom();
724
d121b396 725 id = &all_ids[count];
a331b5e6
JJ
726
727 r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
d121b396 728 if (r < 0)
a331b5e6 729 continue;
a331b5e6 730
a331b5e6
JJ
731 r = sd_journal_add_match(j, data, length);
732 if (r < 0)
d121b396 733 return r;
a331b5e6
JJ
734
735 r = sd_journal_seek_head(j);
736 if (r < 0)
d121b396 737 return r;
a331b5e6
JJ
738
739 r = sd_journal_next(j);
d121b396
ZJS
740 if (r < 0)
741 return r;
742 else if (r == 0)
743 goto flush;
a331b5e6
JJ
744
745 r = sd_journal_get_realtime_usec(j, &id->timestamp);
746 if (r < 0)
d121b396 747 return r;
a331b5e6 748
d121b396 749 if (sd_id128_equal(id->id, *boot_id))
a331b5e6 750 ref_boot_id = *id;
a331b5e6 751
d121b396
ZJS
752 count++;
753 flush:
754 sd_journal_flush_matches(j);
a331b5e6
JJ
755 }
756
d121b396 757 qsort(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
a331b5e6 758
d121b396
ZJS
759 if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
760 if (relative > (int) count || relative <= -(int)count)
761 return -EADDRNOTAVAIL;
a331b5e6 762
d121b396
ZJS
763 *boot_id = all_ids[(relative <= 0)*count + relative - 1].id;
764 } else {
765 id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
a331b5e6 766
d121b396
ZJS
767 if (!id ||
768 relative <= 0 ? (id - all_ids) + relative < 0 :
a54e3b3d 769 (id - all_ids) + relative >= (int) count)
d121b396
ZJS
770 return -EADDRNOTAVAIL;
771
772 *boot_id = (id + relative)->id;
773 }
a963990f 774
a331b5e6
JJ
775 return 0;
776}
777
778static int add_boot(sd_journal *j) {
779 char match[9+32+1] = "_BOOT_ID=";
d121b396
ZJS
780 char *offset;
781 sd_id128_t boot_id = SD_ID128_NULL;
a331b5e6
JJ
782 int r, relative = 0;
783
784 assert(j);
785
d121b396 786 if (!arg_boot)
a331b5e6
JJ
787 return 0;
788
d121b396
ZJS
789 if (!arg_boot_descriptor)
790 return add_match_this_boot(j);
a331b5e6 791
d121b396
ZJS
792 if (strlen(arg_boot_descriptor) >= 32) {
793 char tmp = arg_boot_descriptor[32];
794 arg_boot_descriptor[32] = '\0';
795 r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
796 arg_boot_descriptor[32] = tmp;
797
798 if (r < 0) {
799 log_error("Failed to parse boot ID '%.32s': %s",
800 arg_boot_descriptor, strerror(-r));
801 return r;
a331b5e6 802 }
a331b5e6 803
d121b396
ZJS
804 offset = arg_boot_descriptor + 32;
805
a54e3b3d 806 if (*offset && *offset != '-' && *offset != '+') {
d121b396
ZJS
807 log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
808 return -EINVAL;
a331b5e6 809 }
d121b396
ZJS
810 } else
811 offset = arg_boot_descriptor;
812
813 if (*offset) {
814 r = safe_atoi(offset, &relative);
a331b5e6 815 if (r < 0) {
d121b396
ZJS
816 log_error("Failed to parse relative boot ID number '%s'", offset);
817 return -EINVAL;
a331b5e6
JJ
818 }
819 }
820
821 r = get_relative_boot_id(j, &boot_id, relative);
822 if (r < 0) {
d121b396
ZJS
823 if (sd_id128_equal(boot_id, SD_ID128_NULL))
824 log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
825 else
826 log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
827 SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
a331b5e6 828 return r;
a331b5e6
JJ
829 }
830
831 sd_id128_to_string(boot_id, match + 9);
d121b396
ZJS
832
833 r = sd_journal_add_match(j, match, sizeof(match) - 1);
a331b5e6
JJ
834 if (r < 0) {
835 log_error("Failed to add match: %s", strerror(-r));
836 return r;
837 }
838
839 r = sd_journal_add_conjunction(j);
840 if (r < 0)
841 return r;
842
843 return 0;
a963990f
LP
844}
845
99271804
ZJS
846static int add_dmesg(sd_journal *j) {
847 int r;
848 assert(j);
849
850 if (!arg_dmesg)
851 return 0;
852
853 r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel"));
854 if (r < 0) {
855 log_error("Failed to add match: %s", strerror(-r));
856 return r;
857 }
858
859 r = sd_journal_add_conjunction(j);
860 if (r < 0)
861 return r;
862
863 return 0;
864}
865
b9e40524 866static int add_units(sd_journal *j) {
4750fade 867 _cleanup_free_ char *u = NULL;
c3f60ec5 868 int r;
b9e40524 869 char **i;
c3f60ec5
LP
870
871 assert(j);
872
b9e40524
HH
873 STRV_FOREACH(i, arg_system_units) {
874 u = unit_name_mangle(*i);
875 if (!u)
876 return log_oom();
877 r = add_matches_for_unit(j, u);
878 if (r < 0)
879 return r;
880 r = sd_journal_add_disjunction(j);
881 if (r < 0)
882 return r;
883 }
c3f60ec5 884
b9e40524
HH
885 STRV_FOREACH(i, arg_user_units) {
886 u = unit_name_mangle(*i);
887 if (!u)
888 return log_oom();
c3f60ec5 889
7199aa96 890 r = add_matches_for_user_unit(j, u, getuid());
b9e40524
HH
891 if (r < 0)
892 return r;
893
894 r = sd_journal_add_disjunction(j);
895 if (r < 0)
896 return r;
897
898 }
c3f60ec5 899
cd34b3c6
HH
900 r = sd_journal_add_conjunction(j);
901 if (r < 0)
902 return r;
903
c3f60ec5
LP
904 return 0;
905}
906
941e990d
LP
907static int add_priorities(sd_journal *j) {
908 char match[] = "PRIORITY=0";
909 int i, r;
941e990d
LP
910 assert(j);
911
912 if (arg_priorities == 0xFF)
913 return 0;
914
915 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
916 if (arg_priorities & (1 << i)) {
917 match[sizeof(match)-2] = '0' + i;
918
941e990d
LP
919 r = sd_journal_add_match(j, match, strlen(match));
920 if (r < 0) {
921 log_error("Failed to add match: %s", strerror(-r));
922 return r;
923 }
924 }
925
cd34b3c6
HH
926 r = sd_journal_add_conjunction(j);
927 if (r < 0)
928 return r;
929
941e990d
LP
930 return 0;
931}
932
7560fffc
LP
933static int setup_keys(void) {
934#ifdef HAVE_GCRYPT
935 size_t mpk_size, seed_size, state_size, i;
936 uint8_t *mpk, *seed, *state;
937 ssize_t l;
f982e6f7 938 int fd = -1, r, attr = 0;
7560fffc
LP
939 sd_id128_t machine, boot;
940 char *p = NULL, *k = NULL;
baed47c3 941 struct FSSHeader h;
14d10188 942 uint64_t n;
b98e3866
SL
943 struct stat st;
944
945 r = stat("/var/log/journal", &st);
946 if (r < 0 && errno != ENOENT && errno != ENOTDIR) {
947 log_error("stat(\"%s\") failed: %m", "/var/log/journal");
948 return -errno;
949 }
950
951 if (r < 0 || !S_ISDIR(st.st_mode)) {
952 log_error("%s is not a directory, must be using persistent logging for FSS.",
953 "/var/log/journal");
954 return r < 0 ? -errno : -ENOTDIR;
955 }
7560fffc
LP
956
957 r = sd_id128_get_machine(&machine);
958 if (r < 0) {
959 log_error("Failed to get machine ID: %s", strerror(-r));
960 return r;
961 }
962
963 r = sd_id128_get_boot(&boot);
964 if (r < 0) {
965 log_error("Failed to get boot ID: %s", strerror(-r));
966 return r;
967 }
968
baed47c3 969 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
970 SD_ID128_FORMAT_VAL(machine)) < 0)
971 return log_oom();
972
973 if (access(p, F_OK) >= 0) {
b8547c10
SL
974 if (arg_force) {
975 r = unlink(p);
976 if (r < 0) {
977 log_error("unlink(\"%s\") failed: %m", p);
978 r = -errno;
979 goto finish;
980 }
981 } else {
982 log_error("Sealing key file %s exists already. (--force to recreate)", p);
983 r = -EEXIST;
984 goto finish;
985 }
7560fffc
LP
986 }
987
baed47c3 988 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
989 SD_ID128_FORMAT_VAL(machine)) < 0) {
990 r = log_oom();
991 goto finish;
992 }
993
994 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
995 mpk = alloca(mpk_size);
996
997 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
998 seed = alloca(seed_size);
999
1000 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
1001 state = alloca(state_size);
1002
1003 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1004 if (fd < 0) {
1005 log_error("Failed to open /dev/random: %m");
1006 r = -errno;
1007 goto finish;
1008 }
1009
1010 log_info("Generating seed...");
1011 l = loop_read(fd, seed, seed_size, true);
1012 if (l < 0 || (size_t) l != seed_size) {
1013 log_error("Failed to read random seed: %s", strerror(EIO));
1014 r = -EIO;
1015 goto finish;
1016 }
1017
1018 log_info("Generating key pair...");
1019 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
1020
baed47c3 1021 log_info("Generating sealing key...");
7560fffc
LP
1022 FSPRG_GenState0(state, mpk, seed, seed_size);
1023
baed47c3
LP
1024 assert(arg_interval > 0);
1025
7560fffc 1026 n = now(CLOCK_REALTIME);
baed47c3 1027 n /= arg_interval;
7560fffc
LP
1028
1029 close_nointr_nofail(fd);
1030 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
1031 if (fd < 0) {
1032 log_error("Failed to open %s: %m", k);
1033 r = -errno;
1034 goto finish;
1035 }
1036
f982e6f7
LP
1037 /* Enable secure remove, exclusion from dump, synchronous
1038 * writing and in-place updating */
1039 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
1040 log_warning("FS_IOC_GETFLAGS failed: %m");
1041
1042 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
1043
1044 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
1045 log_warning("FS_IOC_SETFLAGS failed: %m");
1046
7560fffc
LP
1047 zero(h);
1048 memcpy(h.signature, "KSHHRHLP", 8);
1049 h.machine_id = machine;
1050 h.boot_id = boot;
1051 h.header_size = htole64(sizeof(h));
baed47c3
LP
1052 h.start_usec = htole64(n * arg_interval);
1053 h.interval_usec = htole64(arg_interval);
1054 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
1055 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
1056
1057 l = loop_write(fd, &h, sizeof(h), false);
1058 if (l < 0 || (size_t) l != sizeof(h)) {
1059 log_error("Failed to write header: %s", strerror(EIO));
1060 r = -EIO;
1061 goto finish;
1062 }
1063
1064 l = loop_write(fd, state, state_size, false);
1065 if (l < 0 || (size_t) l != state_size) {
1066 log_error("Failed to write state: %s", strerror(EIO));
1067 r = -EIO;
1068 goto finish;
1069 }
1070
1071 if (link(k, p) < 0) {
1072 log_error("Failed to link file: %m");
1073 r = -errno;
1074 goto finish;
1075 }
1076
8481248b 1077 if (on_tty()) {
7560fffc
LP
1078 fprintf(stderr,
1079 "\n"
baed47c3 1080 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
1081 "the following local file. This key file is automatically updated when the\n"
1082 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
1083 "\n"
1084 "\t%s\n"
1085 "\n"
baed47c3
LP
1086 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
1087 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
1088 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
1089 fflush(stderr);
1090 }
1091 for (i = 0; i < seed_size; i++) {
1092 if (i > 0 && i % 3 == 0)
1093 putchar('-');
1094 printf("%02x", ((uint8_t*) seed)[i]);
1095 }
1096
baed47c3
LP
1097 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
1098
8481248b 1099 if (on_tty()) {
f6a971bc 1100 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 1101
baed47c3
LP
1102 fprintf(stderr,
1103 ANSI_HIGHLIGHT_OFF "\n"
1104 "The sealing key is automatically changed every %s.\n",
2fa4092c 1105 format_timespan(tsb, sizeof(tsb), arg_interval, 0));
f6a971bc
LP
1106
1107 hn = gethostname_malloc();
1108
1109 if (hn) {
e724b063 1110 hostname_cleanup(hn, false);
adac1c93 1111 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 1112 } else
adac1c93 1113 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
1114
1115#ifdef HAVE_QRENCODE
cf5a3432 1116 /* If this is not an UTF-8 system don't print any QR codes */
09017585 1117 if (is_locale_utf8()) {
cf5a3432
LP
1118 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
1119 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
1120 }
f6a971bc
LP
1121#endif
1122 free(hn);
baed47c3 1123 }
7560fffc
LP
1124
1125 r = 0;
1126
1127finish:
1128 if (fd >= 0)
1129 close_nointr_nofail(fd);
1130
1131 if (k) {
1132 unlink(k);
1133 free(k);
1134 }
1135
1136 free(p);
1137
1138 return r;
1139#else
feb12d3e
LP
1140 log_error("Forward-secure sealing not available.");
1141 return -ENOTSUP;
7560fffc
LP
1142#endif
1143}
1144
beec0085
LP
1145static int verify(sd_journal *j) {
1146 int r = 0;
1147 Iterator i;
1148 JournalFile *f;
1149
1150 assert(j);
1151
cedb42bb
LP
1152 log_show_color(true);
1153
beec0085
LP
1154 HASHMAP_FOREACH(f, j->files, i) {
1155 int k;
2a7b539a 1156 usec_t first, validated, last;
beec0085 1157
56e81f7c 1158#ifdef HAVE_GCRYPT
feb12d3e 1159 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 1160 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 1161#endif
4da416aa 1162
2a7b539a 1163 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 1164 if (k == -EINVAL) {
baed47c3 1165 /* If the key was invalid give up right-away. */
56e81f7c
LP
1166 return k;
1167 } else if (k < 0) {
beec0085 1168 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 1169 r = k;
6c7be122
LP
1170 } else {
1171 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 1172 log_info("PASS: %s", f->path);
6c7be122 1173
c0ca7aee 1174 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 1175 if (validated > 0) {
c0ca7aee 1176 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
1177 format_timestamp(a, sizeof(a), first),
1178 format_timestamp(b, sizeof(b), validated),
2fa4092c 1179 format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
2a7b539a 1180 } else if (last > 0)
c0ca7aee 1181 log_info("=> No sealing yet, %s of entries not sealed.",
2fa4092c 1182 format_timespan(c, sizeof(c), last - first, 0));
c0ca7aee
LP
1183 else
1184 log_info("=> No sealing yet, no entries in file.");
1185 }
6c7be122 1186 }
beec0085
LP
1187 }
1188
1189 return r;
1190}
1191
15804ceb 1192#ifdef HAVE_ACL
6fe391c5
ZJS
1193static int access_check_var_log_journal(sd_journal *j) {
1194 _cleanup_strv_free_ char **g = NULL;
1195 bool have_access;
1196 int r;
1197
1198 assert(j);
1199
1200 have_access = in_group("systemd-journal") > 0;
1201
1202 if (!have_access) {
1203 /* Let's enumerate all groups from the default ACL of
1204 * the directory, which generally should allow access
1205 * to most journal files too */
1206 r = search_acl_groups(&g, "/var/log/journal/", &have_access);
1207 if (r < 0)
1208 return r;
15804ceb
LP
1209 }
1210
6fe391c5 1211 if (!have_access) {
4468addc 1212
6fe391c5 1213 if (strv_isempty(g))
3ac251b8
LP
1214 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
1215 " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
1216 " turn off this notice.");
6fe391c5
ZJS
1217 else {
1218 _cleanup_free_ char *s = NULL;
4468addc 1219
6fe391c5 1220 r = strv_extend(&g, "systemd-journal");
478c8269 1221 if (r < 0)
6fe391c5
ZJS
1222 return log_oom();
1223
1224 strv_sort(g);
1225 strv_uniq(g);
1226
1227 s = strv_join(g, "', '");
1228 if (!s)
1229 return log_oom();
1230
1231 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
3ac251b8
LP
1232 " Users in the groups '%s' can see all messages.\n"
1233 " Pass -q to turn off this notice.", s);
4468addc 1234 }
6fe391c5 1235 }
4468addc 1236
6fe391c5
ZJS
1237 return 0;
1238}
1239#endif
4468addc 1240
6fe391c5 1241static int access_check(sd_journal *j) {
6fe391c5 1242 Iterator it;
3ac251b8 1243 void *code;
6fe391c5 1244 int r = 0;
4468addc 1245
6fe391c5 1246 assert(j);
4468addc 1247
6fe391c5
ZJS
1248 if (set_isempty(j->errors)) {
1249 if (hashmap_isempty(j->files))
3ac251b8 1250 log_notice("No journal files were found.");
6fe391c5
ZJS
1251 return 0;
1252 }
4468addc 1253
3ac251b8 1254 if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
6fe391c5
ZJS
1255#ifdef HAVE_ACL
1256 /* If /var/log/journal doesn't even exist,
3ac251b8 1257 * unprivileged users have no access at all */
6fe391c5
ZJS
1258 if (access("/var/log/journal", F_OK) < 0 &&
1259 geteuid() != 0 &&
1260 in_group("systemd-journal") <= 0) {
3ac251b8
LP
1261 log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
1262 "enabled. Users in the 'systemd-journal' group may always access messages.");
6fe391c5
ZJS
1263 return -EACCES;
1264 }
4468addc 1265
6fe391c5
ZJS
1266 /* If /var/log/journal exists, try to pring a nice
1267 notice if the user lacks access to it */
1268 if (!arg_quiet && geteuid() != 0) {
1269 r = access_check_var_log_journal(j);
1270 if (r < 0)
1271 return r;
4468addc 1272 }
15804ceb 1273#else
3ac251b8
LP
1274 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
1275 log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
1276 "group may access messages.");
1277 return -EACCES;
1278 }
15804ceb 1279#endif
3ac251b8 1280
6fe391c5 1281 if (hashmap_isempty(j->files)) {
3ac251b8 1282 log_error("No journal files were opened due to insufficient permissions.");
6fe391c5
ZJS
1283 r = -EACCES;
1284 }
1285 }
15804ceb 1286
6fe391c5 1287 SET_FOREACH(code, j->errors, it) {
3ac251b8
LP
1288 int err;
1289
1290 err = -PTR_TO_INT(code);
6fe391c5 1291 assert(err > 0);
3ac251b8 1292
6fe391c5
ZJS
1293 if (err != EACCES)
1294 log_warning("Error was encountered while opening journal files: %s",
1295 strerror(err));
1296 }
1297
6fe391c5 1298 return r;
15804ceb
LP
1299}
1300
a963990f
LP
1301int main(int argc, char *argv[]) {
1302 int r;
7fd1b19b 1303 _cleanup_journal_close_ sd_journal*j = NULL;
a963990f 1304 bool need_seek = false;
14a65d65 1305 sd_id128_t previous_boot_id;
67e04a48
ZJS
1306 bool previous_boot_id_valid = false, first_line = true;
1307 int n_shown = 0;
94e0bd7d 1308 bool ellipsized = false;
a963990f 1309
a9cdc94f 1310 setlocale(LC_ALL, "");
a963990f
LP
1311 log_parse_environment();
1312 log_open();
1313
1314 r = parse_argv(argc, argv);
1315 if (r <= 0)
1316 goto finish;
1317
ed757c0c
LP
1318 signal(SIGWINCH, columns_lines_cache_reset);
1319
7560fffc 1320 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
1321 r = generate_new_id128();
1322 goto finish;
1323 }
1324
7560fffc
LP
1325 if (arg_action == ACTION_SETUP_KEYS) {
1326 r = setup_keys();
1327 goto finish;
1328 }
1329
844ec79b
ZJS
1330 if (arg_action == ACTION_UPDATE_CATALOG ||
1331 arg_action == ACTION_LIST_CATALOG ||
1332 arg_action == ACTION_DUMP_CATALOG) {
1333
0db80948 1334 const char* database = CATALOG_DATABASE;
7fd1b19b 1335 _cleanup_free_ char *copy = NULL;
0db80948
ZJS
1336 if (arg_root) {
1337 copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
3a819b1b 1338 if (!copy) {
0db80948
ZJS
1339 r = log_oom();
1340 goto finish;
1341 }
1342 path_kill_slashes(copy);
1343 database = copy;
13cbf3a5
ZJS
1344 }
1345
844ec79b 1346 if (arg_action == ACTION_UPDATE_CATALOG) {
13cbf3a5 1347 r = catalog_update(database, arg_root, catalog_file_dirs);
844ec79b
ZJS
1348 if (r < 0)
1349 log_error("Failed to list catalog: %s", strerror(-r));
1350 } else {
1351 bool oneline = arg_action == ACTION_LIST_CATALOG;
1352
1353 if (optind < argc)
13cbf3a5 1354 r = catalog_list_items(stdout, database,
844ec79b
ZJS
1355 oneline, argv + optind);
1356 else
13cbf3a5 1357 r = catalog_list(stdout, database, oneline);
844ec79b
ZJS
1358 if (r < 0)
1359 log_error("Failed to list catalog: %s", strerror(-r));
1360 }
d4205751 1361
d4205751
LP
1362 goto finish;
1363 }
1364
a963990f 1365 if (arg_directory)
3f3a438f 1366 r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
8d98da3f
ZJS
1367 else if (arg_file)
1368 r = sd_journal_open_files(&j, (const char**) arg_file, 0);
a963990f 1369 else
3f3a438f 1370 r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
a963990f 1371 if (r < 0) {
8d98da3f
ZJS
1372 log_error("Failed to open %s: %s",
1373 arg_directory ? arg_directory : arg_file ? "files" : "journal",
1374 strerror(-r));
763c7aa2 1375 return EXIT_FAILURE;
a963990f
LP
1376 }
1377
6fe391c5
ZJS
1378 r = access_check(j);
1379 if (r < 0)
1380 return EXIT_FAILURE;
1381
beec0085
LP
1382 if (arg_action == ACTION_VERIFY) {
1383 r = verify(j);
1384 goto finish;
1385 }
1386
7560fffc 1387 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e 1388 journal_print_header(j);
763c7aa2 1389 return EXIT_SUCCESS;
dca6219e
LP
1390 }
1391
a1a03e30
LP
1392 if (arg_action == ACTION_DISK_USAGE) {
1393 uint64_t bytes;
1394 char sbytes[FORMAT_BYTES_MAX];
1395
1396 r = sd_journal_get_usage(j, &bytes);
1397 if (r < 0)
763c7aa2 1398 return EXIT_FAILURE;
a1a03e30 1399
763c7aa2
ZJS
1400 printf("Journals take up %s on disk.\n",
1401 format_bytes(sbytes, sizeof(sbytes), bytes));
1402 return EXIT_SUCCESS;
a1a03e30
LP
1403 }
1404
a331b5e6
JJ
1405 /* add_boot() must be called first!
1406 * It may need to seek the journal to find parent boot IDs. */
1407 r = add_boot(j);
a963990f 1408 if (r < 0)
763c7aa2 1409 return EXIT_FAILURE;
a963990f 1410
99271804
ZJS
1411 r = add_dmesg(j);
1412 if (r < 0)
1413 return EXIT_FAILURE;
1414
b9e40524
HH
1415 r = add_units(j);
1416 strv_free(arg_system_units);
1417 strv_free(arg_user_units);
1418
c3f60ec5 1419 if (r < 0)
763c7aa2 1420 return EXIT_FAILURE;
c3f60ec5 1421
cd34b3c6 1422 r = add_priorities(j);
a963990f 1423 if (r < 0)
763c7aa2 1424 return EXIT_FAILURE;
a963990f 1425
cd34b3c6 1426 r = add_matches(j, argv + optind);
941e990d 1427 if (r < 0)
763c7aa2 1428 return EXIT_FAILURE;
941e990d 1429
4ad16808
ZJS
1430 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1431 _cleanup_free_ char *filter;
1432
1433 filter = journal_make_match_string(j);
1434 log_debug("Journal filter: %s", filter);
1435 }
67e04a48 1436
15119c16
LP
1437 if (arg_field) {
1438 const void *data;
1439 size_t size;
1440
21ae4593
ZJS
1441 r = sd_journal_set_data_threshold(j, 0);
1442 if (r < 0) {
1443 log_error("Failed to unset data size threshold");
1444 return EXIT_FAILURE;
1445 }
1446
15119c16
LP
1447 r = sd_journal_query_unique(j, arg_field);
1448 if (r < 0) {
1449 log_error("Failed to query unique data objects: %s", strerror(-r));
763c7aa2 1450 return EXIT_FAILURE;
15119c16
LP
1451 }
1452
1453 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1454 const void *eq;
1455
67e04a48 1456 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
1457 break;
1458
15119c16
LP
1459 eq = memchr(data, '=', size);
1460 if (eq)
1461 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1462 else
1463 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
1464
1465 n_shown ++;
15119c16
LP
1466 }
1467
763c7aa2 1468 return EXIT_SUCCESS;
15119c16
LP
1469 }
1470
8d98da3f
ZJS
1471 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1472 if (arg_follow) {
1473 r = sd_journal_get_fd(j);
1474 if (r < 0)
1475 return EXIT_FAILURE;
1476 }
1477
248fc619
ZJS
1478 if (arg_cursor || arg_after_cursor) {
1479 r = sd_journal_seek_cursor(j, arg_cursor ? arg_cursor : arg_after_cursor);
08984293 1480 if (r < 0) {
cfbc22ab 1481 log_error("Failed to seek to cursor: %s", strerror(-r));
763c7aa2 1482 return EXIT_FAILURE;
08984293 1483 }
d89d6c86 1484 if (!arg_reverse)
248fc619 1485 r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
d89d6c86 1486 else
248fc619
ZJS
1487 r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
1488
1489 if (arg_after_cursor && r < 2 && !arg_follow)
1490 /* We couldn't find the next entry after the cursor. */
1491 arg_lines = 0;
08984293 1492
d89d6c86 1493 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1494 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1495 if (r < 0) {
cfbc22ab 1496 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1497 return EXIT_FAILURE;
8f14c832 1498 }
8f14c832
LP
1499 r = sd_journal_next(j);
1500
d89d6c86
LN
1501 } else if (arg_until_set && arg_reverse) {
1502 r = sd_journal_seek_realtime_usec(j, arg_until);
1503 if (r < 0) {
1504 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1505 return EXIT_FAILURE;
d89d6c86
LN
1506 }
1507 r = sd_journal_previous(j);
1508
67e04a48 1509 } else if (arg_lines >= 0) {
2100675e
LP
1510 r = sd_journal_seek_tail(j);
1511 if (r < 0) {
1512 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1513 return EXIT_FAILURE;
2100675e
LP
1514 }
1515
1516 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1517
d89d6c86
LN
1518 } else if (arg_reverse) {
1519 r = sd_journal_seek_tail(j);
1520 if (r < 0) {
1521 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1522 return EXIT_FAILURE;
d89d6c86
LN
1523 }
1524
1525 r = sd_journal_previous(j);
1526
2100675e
LP
1527 } else {
1528 r = sd_journal_seek_head(j);
1529 if (r < 0) {
1530 log_error("Failed to seek to head: %s", strerror(-r));
763c7aa2 1531 return EXIT_FAILURE;
2100675e 1532 }
6f003b43
LP
1533
1534 r = sd_journal_next(j);
1535 }
1536
1537 if (r < 0) {
1538 log_error("Failed to iterate through journal: %s", strerror(-r));
763c7aa2 1539 return EXIT_FAILURE;
50f20cfd 1540 }
87d2c1ff 1541
f89a3b6f 1542 if (!arg_no_pager && !arg_follow)
1b12a7b5 1543 pager_open(arg_pager_end);
0d43c694 1544
cfbc22ab
LP
1545 if (!arg_quiet) {
1546 usec_t start, end;
1547 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1548
1549 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1550 if (r < 0) {
1551 log_error("Failed to get cutoff: %s", strerror(-r));
1552 goto finish;
1553 }
1554
1555 if (r > 0) {
1556 if (arg_follow)
9048b11f
LP
1557 printf("-- Logs begin at %s. --\n",
1558 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1559 else
9048b11f 1560 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1561 format_timestamp(start_buf, sizeof(start_buf), start),
1562 format_timestamp(end_buf, sizeof(end_buf), end));
1563 }
1564 }
1565
50f20cfd 1566 for (;;) {
67e04a48 1567 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1568 int flags;
1569
6f003b43 1570 if (need_seek) {
99613ec5 1571 if (!arg_reverse)
d89d6c86
LN
1572 r = sd_journal_next(j);
1573 else
1574 r = sd_journal_previous(j);
6f003b43
LP
1575 if (r < 0) {
1576 log_error("Failed to iterate through journal: %s", strerror(-r));
1577 goto finish;
1578 }
a72b6353
ZJS
1579 if (r == 0)
1580 break;
0d43c694
LP
1581 }
1582
d89d6c86 1583 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1584 usec_t usec;
1585
1586 r = sd_journal_get_realtime_usec(j, &usec);
1587 if (r < 0) {
1588 log_error("Failed to determine timestamp: %s", strerror(-r));
1589 goto finish;
1590 }
3ba09ee8
PF
1591 if (usec > arg_until)
1592 goto finish;
cfbc22ab
LP
1593 }
1594
d89d6c86
LN
1595 if (arg_since_set && arg_reverse) {
1596 usec_t usec;
1597
1598 r = sd_journal_get_realtime_usec(j, &usec);
1599 if (r < 0) {
1600 log_error("Failed to determine timestamp: %s", strerror(-r));
1601 goto finish;
1602 }
1603 if (usec < arg_since)
1604 goto finish;
1605 }
1606
cd931c0a
LP
1607 if (!arg_merge) {
1608 sd_id128_t boot_id;
14a65d65 1609
cd931c0a
LP
1610 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1611 if (r >= 0) {
1612 if (previous_boot_id_valid &&
1613 !sd_id128_equal(boot_id, previous_boot_id))
0b5a519c
DS
1614 printf("%s-- Reboot --%s\n",
1615 ansi_highlight(), ansi_highlight_off());
cd931c0a
LP
1616
1617 previous_boot_id = boot_id;
1618 previous_boot_id_valid = true;
1619 }
14a65d65
LP
1620 }
1621
cfbc22ab 1622 flags =
cd4b13e0 1623 arg_all * OUTPUT_SHOW_ALL |
e3657ecd 1624 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
d4205751
LP
1625 on_tty() * OUTPUT_COLOR |
1626 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1627
94e0bd7d 1628 r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
a72b6353
ZJS
1629 need_seek = true;
1630 if (r == -EADDRNOTAVAIL)
1631 break;
1632 else if (r < 0 || ferror(stdout))
72f59706 1633 goto finish;
6f003b43 1634
cfbc22ab 1635 n_shown++;
87d2c1ff
LP
1636 }
1637
248fc619
ZJS
1638 if (!arg_follow) {
1639 if (arg_show_cursor) {
1640 _cleanup_free_ char *cursor = NULL;
1641
1642 r = sd_journal_get_cursor(j, &cursor);
1643 if (r < 0 && r != -EADDRNOTAVAIL)
1644 log_error("Failed to get cursor: %s", strerror(-r));
1645 else if (r >= 0)
1646 printf("-- cursor: %s\n", cursor);
1647 }
1648
50f20cfd 1649 break;
248fc619 1650 }
50f20cfd 1651
e02d1cf7 1652 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1653 if (r < 0) {
7a69007a 1654 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1655 goto finish;
1656 }
67e04a48
ZJS
1657
1658 first_line = false;
de190aef 1659 }
87d2c1ff
LP
1660
1661finish:
0d43c694
LP
1662 pager_close();
1663
3fbf9cbb 1664 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1665}