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