]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journal: split up journal-file.c
[thirdparty/systemd.git] / src / journal / journalctl.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/poll.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <sys/stat.h>
33
34 #include <systemd/sd-journal.h>
35
36 #include "log.h"
37 #include "util.h"
38 #include "path-util.h"
39 #include "build.h"
40 #include "pager.h"
41 #include "logs-show.h"
42 #include "strv.h"
43 #include "journal-internal.h"
44 #include "fsprg.h"
45 #include "journal-def.h"
46 #include "journal-verify.h"
47
48 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
49
50 static OutputMode arg_output = OUTPUT_SHORT;
51 static bool arg_follow = false;
52 static bool arg_show_all = false;
53 static bool arg_no_pager = false;
54 static int arg_lines = -1;
55 static bool arg_no_tail = false;
56 static bool arg_quiet = false;
57 static bool arg_local = false;
58 static bool arg_this_boot = false;
59 static const char *arg_directory = NULL;
60 static int arg_priorities = 0xFF;
61
62 static enum {
63 ACTION_SHOW,
64 ACTION_NEW_ID128,
65 ACTION_PRINT_HEADER,
66 ACTION_SETUP_KEYS,
67 ACTION_VERIFY
68 } arg_action = ACTION_SHOW;
69
70 static int help(void) {
71
72 printf("%s [OPTIONS...] [MATCH]\n\n"
73 "Send control commands to or query the journal.\n\n"
74 " -h --help Show this help\n"
75 " --version Show package version\n"
76 " --no-pager Do not pipe output into a pager\n"
77 " -a --all Show all fields, including long and unprintable\n"
78 " -f --follow Follow journal\n"
79 " -n --lines=INTEGER Journal entries to show\n"
80 " --no-tail Show all lines, even in follow mode\n"
81 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
82 " verbose, export, json, cat)\n"
83 " -q --quiet Don't show privilege warning\n"
84 " -l --local Only local entries\n"
85 " -b --this-boot Show data only from current boot\n"
86 " -D --directory=PATH Show journal files from directory\n"
87 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
88 "Commands:\n"
89 " --new-id128 Generate a new 128 Bit ID\n"
90 " --header Show journal header information\n"
91 " --setup-keys Generate new FSPRG key pair\n"
92 " --verify Verify journal file consistency\n",
93 program_invocation_short_name);
94
95 return 0;
96 }
97
98 static int parse_argv(int argc, char *argv[]) {
99
100 enum {
101 ARG_VERSION = 0x100,
102 ARG_NO_PAGER,
103 ARG_NO_TAIL,
104 ARG_NEW_ID128,
105 ARG_HEADER,
106 ARG_SETUP_KEYS,
107 ARG_VERIFY
108 };
109
110 static const struct option options[] = {
111 { "help", no_argument, NULL, 'h' },
112 { "version" , no_argument, NULL, ARG_VERSION },
113 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
114 { "follow", no_argument, NULL, 'f' },
115 { "output", required_argument, NULL, 'o' },
116 { "all", no_argument, NULL, 'a' },
117 { "lines", required_argument, NULL, 'n' },
118 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
119 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
120 { "quiet", no_argument, NULL, 'q' },
121 { "local", no_argument, NULL, 'l' },
122 { "this-boot", no_argument, NULL, 'b' },
123 { "directory", required_argument, NULL, 'D' },
124 { "header", no_argument, NULL, ARG_HEADER },
125 { "priority", no_argument, NULL, 'p' },
126 { "setup-keys",no_argument, NULL, ARG_SETUP_KEYS},
127 { "verify", no_argument, NULL, ARG_VERIFY },
128 { NULL, 0, NULL, 0 }
129 };
130
131 int c, r;
132
133 assert(argc >= 0);
134 assert(argv);
135
136 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
137
138 switch (c) {
139
140 case 'h':
141 help();
142 return 0;
143
144 case ARG_VERSION:
145 puts(PACKAGE_STRING);
146 puts(DISTRIBUTION);
147 puts(SYSTEMD_FEATURES);
148 return 0;
149
150 case ARG_NO_PAGER:
151 arg_no_pager = true;
152 break;
153
154 case 'f':
155 arg_follow = true;
156 break;
157
158 case 'o':
159 arg_output = output_mode_from_string(optarg);
160 if (arg_output < 0) {
161 log_error("Unknown output '%s'.", optarg);
162 return -EINVAL;
163 }
164
165 break;
166
167 case 'a':
168 arg_show_all = true;
169 break;
170
171 case 'n':
172 r = safe_atoi(optarg, &arg_lines);
173 if (r < 0 || arg_lines < 0) {
174 log_error("Failed to parse lines '%s'", optarg);
175 return -EINVAL;
176 }
177 break;
178
179 case ARG_NO_TAIL:
180 arg_no_tail = true;
181 break;
182
183 case ARG_NEW_ID128:
184 arg_action = ACTION_NEW_ID128;
185 break;
186
187 case 'q':
188 arg_quiet = true;
189 break;
190
191 case 'l':
192 arg_local = true;
193 break;
194
195 case 'b':
196 arg_this_boot = true;
197 break;
198
199 case 'D':
200 arg_directory = optarg;
201 break;
202
203 case ARG_HEADER:
204 arg_action = ACTION_PRINT_HEADER;
205 break;
206
207 case ARG_SETUP_KEYS:
208 arg_action = ACTION_SETUP_KEYS;
209 break;
210
211 case ARG_VERIFY:
212 arg_action = ACTION_VERIFY;
213 break;
214
215 case 'p': {
216 const char *dots;
217
218 dots = strstr(optarg, "..");
219 if (dots) {
220 char *a;
221 int from, to, i;
222
223 /* a range */
224 a = strndup(optarg, dots - optarg);
225 if (!a)
226 return log_oom();
227
228 from = log_level_from_string(a);
229 to = log_level_from_string(dots + 2);
230 free(a);
231
232 if (from < 0 || to < 0) {
233 log_error("Failed to parse log level range %s", optarg);
234 return -EINVAL;
235 }
236
237 arg_priorities = 0;
238
239 if (from < to) {
240 for (i = from; i <= to; i++)
241 arg_priorities |= 1 << i;
242 } else {
243 for (i = to; i <= from; i++)
244 arg_priorities |= 1 << i;
245 }
246
247 } else {
248 int p, i;
249
250 p = log_level_from_string(optarg);
251 if (p < 0) {
252 log_error("Unknown log level %s", optarg);
253 return -EINVAL;
254 }
255
256 arg_priorities = 0;
257
258 for (i = 0; i <= p; i++)
259 arg_priorities |= 1 << i;
260 }
261
262 break;
263 }
264
265 case '?':
266 return -EINVAL;
267
268 default:
269 log_error("Unknown option code %c", c);
270 return -EINVAL;
271 }
272 }
273
274 if (arg_follow && !arg_no_tail && arg_lines < 0)
275 arg_lines = 10;
276
277 return 1;
278 }
279
280 static bool on_tty(void) {
281 static int t = -1;
282
283 /* Note that this is invoked relatively early, before we start
284 * the pager. That means the value we return reflects whether
285 * we originally were started on a tty, not if we currently
286 * are. But this is intended, since we want colour and so on
287 * when run in our own pager. */
288
289 if (_unlikely_(t < 0))
290 t = isatty(STDOUT_FILENO) > 0;
291
292 return t;
293 }
294
295 static int generate_new_id128(void) {
296 sd_id128_t id;
297 int r;
298 unsigned i;
299
300 r = sd_id128_randomize(&id);
301 if (r < 0) {
302 log_error("Failed to generate ID: %s", strerror(-r));
303 return r;
304 }
305
306 printf("As string:\n"
307 SD_ID128_FORMAT_STR "\n\n"
308 "As UUID:\n"
309 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
310 "As macro:\n"
311 "#define MESSAGE_XYZ SD_ID128_MAKE(",
312 SD_ID128_FORMAT_VAL(id),
313 SD_ID128_FORMAT_VAL(id));
314
315 for (i = 0; i < 16; i++)
316 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
317
318 fputs(")\n", stdout);
319
320 return 0;
321 }
322
323 static int add_matches(sd_journal *j, char **args) {
324 char **i;
325 int r;
326
327 assert(j);
328
329 STRV_FOREACH(i, args) {
330
331 if (streq(*i, "+"))
332 r = sd_journal_add_disjunction(j);
333 else if (path_is_absolute(*i)) {
334 char *p, *t = NULL;
335 const char *path;
336 struct stat st;
337
338 p = canonicalize_file_name(*i);
339 path = p ? p : *i;
340
341 if (stat(path, &st) < 0) {
342 free(p);
343 log_error("Couldn't stat file: %m");
344 return -errno;
345 }
346
347 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
348 t = strappend("_EXE=", path);
349 else if (S_ISCHR(st.st_mode))
350 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
351 else if (S_ISBLK(st.st_mode))
352 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
353 else {
354 free(p);
355 log_error("File is not a device node, regular file or is not executable: %s", *i);
356 return -EINVAL;
357 }
358
359 free(p);
360
361 if (!t)
362 return log_oom();
363
364 r = sd_journal_add_match(j, t, 0);
365 free(t);
366 } else
367 r = sd_journal_add_match(j, *i, 0);
368
369 if (r < 0) {
370 log_error("Failed to add match '%s': %s", *i, strerror(-r));
371 return r;
372 }
373 }
374
375 return 0;
376 }
377
378 static int add_this_boot(sd_journal *j) {
379 char match[9+32+1] = "_BOOT_ID=";
380 sd_id128_t boot_id;
381 int r;
382
383 assert(j);
384
385 if (!arg_this_boot)
386 return 0;
387
388 r = sd_id128_get_boot(&boot_id);
389 if (r < 0) {
390 log_error("Failed to get boot id: %s", strerror(-r));
391 return r;
392 }
393
394 sd_id128_to_string(boot_id, match + 9);
395 r = sd_journal_add_match(j, match, strlen(match));
396 if (r < 0) {
397 log_error("Failed to add match: %s", strerror(-r));
398 return r;
399 }
400
401 return 0;
402 }
403
404 static int add_priorities(sd_journal *j) {
405 char match[] = "PRIORITY=0";
406 int i, r;
407
408 assert(j);
409
410 if (arg_priorities == 0xFF)
411 return 0;
412
413 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
414 if (arg_priorities & (1 << i)) {
415 match[sizeof(match)-2] = '0' + i;
416
417 log_info("adding match %s", match);
418
419 r = sd_journal_add_match(j, match, strlen(match));
420 if (r < 0) {
421 log_error("Failed to add match: %s", strerror(-r));
422 return r;
423 }
424 }
425
426 return 0;
427 }
428
429 static int setup_keys(void) {
430 #ifdef HAVE_GCRYPT
431 size_t mpk_size, seed_size, state_size, i;
432 uint8_t *mpk, *seed, *state;
433 ssize_t l;
434 int fd = -1, r;
435 sd_id128_t machine, boot;
436 char *p = NULL, *k = NULL;
437 struct FSPRGHeader h;
438 uint64_t n, interval;
439
440 r = sd_id128_get_machine(&machine);
441 if (r < 0) {
442 log_error("Failed to get machine ID: %s", strerror(-r));
443 return r;
444 }
445
446 r = sd_id128_get_boot(&boot);
447 if (r < 0) {
448 log_error("Failed to get boot ID: %s", strerror(-r));
449 return r;
450 }
451
452 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
453 SD_ID128_FORMAT_VAL(machine)) < 0)
454 return log_oom();
455
456 if (access(p, F_OK) >= 0) {
457 log_error("Evolving key file %s exists already.", p);
458 r = -EEXIST;
459 goto finish;
460 }
461
462 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
463 SD_ID128_FORMAT_VAL(machine)) < 0) {
464 r = log_oom();
465 goto finish;
466 }
467
468 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
469 mpk = alloca(mpk_size);
470
471 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
472 seed = alloca(seed_size);
473
474 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
475 state = alloca(state_size);
476
477 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
478 if (fd < 0) {
479 log_error("Failed to open /dev/random: %m");
480 r = -errno;
481 goto finish;
482 }
483
484 log_info("Generating seed...");
485 l = loop_read(fd, seed, seed_size, true);
486 if (l < 0 || (size_t) l != seed_size) {
487 log_error("Failed to read random seed: %s", strerror(EIO));
488 r = -EIO;
489 goto finish;
490 }
491
492 log_info("Generating key pair...");
493 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
494
495 log_info("Generating evolving key...");
496 FSPRG_GenState0(state, mpk, seed, seed_size);
497
498 interval = DEFAULT_FSPRG_INTERVAL_USEC;
499 n = now(CLOCK_REALTIME);
500 n /= interval;
501
502 close_nointr_nofail(fd);
503 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
504 if (fd < 0) {
505 log_error("Failed to open %s: %m", k);
506 r = -errno;
507 goto finish;
508 }
509
510 zero(h);
511 memcpy(h.signature, "KSHHRHLP", 8);
512 h.machine_id = machine;
513 h.boot_id = boot;
514 h.header_size = htole64(sizeof(h));
515 h.fsprg_start_usec = htole64(n * interval);
516 h.fsprg_interval_usec = htole64(interval);
517 h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
518 h.state_size = htole64(state_size);
519
520 l = loop_write(fd, &h, sizeof(h), false);
521 if (l < 0 || (size_t) l != sizeof(h)) {
522 log_error("Failed to write header: %s", strerror(EIO));
523 r = -EIO;
524 goto finish;
525 }
526
527 l = loop_write(fd, state, state_size, false);
528 if (l < 0 || (size_t) l != state_size) {
529 log_error("Failed to write state: %s", strerror(EIO));
530 r = -EIO;
531 goto finish;
532 }
533
534 if (link(k, p) < 0) {
535 log_error("Failed to link file: %m");
536 r = -errno;
537 goto finish;
538 }
539
540 if (isatty(STDOUT_FILENO)) {
541 fprintf(stderr,
542 "\n"
543 "The new key pair has been generated. The evolving key has been written to the\n"
544 "following file. It will be used to protect local journal files. This file does\n"
545 "not need to be kept secret. It should not be used on multiple hosts.\n"
546 "\n"
547 "\t%s\n"
548 "\n"
549 "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
550 "locally on disk, and may be used to verify journal files from this host.\n"
551 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
552 fflush(stderr);
553 }
554 for (i = 0; i < seed_size; i++) {
555 if (i > 0 && i % 3 == 0)
556 putchar('-');
557 printf("%02x", ((uint8_t*) seed)[i]);
558 }
559
560 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
561
562 if (isatty(STDOUT_FILENO))
563 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
564
565 r = 0;
566
567 finish:
568 if (fd >= 0)
569 close_nointr_nofail(fd);
570
571 if (k) {
572 unlink(k);
573 free(k);
574 }
575
576 free(p);
577
578 return r;
579 #else
580 log_error("Forward-secure journal verification not available.");
581 #endif
582 }
583
584 static int verify(sd_journal *j) {
585 int r = 0;
586 Iterator i;
587 JournalFile *f;
588
589 assert(j);
590
591 HASHMAP_FOREACH(f, j->files, i) {
592 int k;
593
594 k = journal_file_verify(f, NULL);
595 if (k < 0) {
596 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
597 r = -r;
598 } else
599 log_info("PASS: %s", f->path);
600 }
601
602 return r;
603 }
604
605 int main(int argc, char *argv[]) {
606 int r;
607 sd_journal *j = NULL;
608 unsigned line = 0;
609 bool need_seek = false;
610 sd_id128_t previous_boot_id;
611 bool previous_boot_id_valid = false;
612 bool have_pager;
613
614 log_parse_environment();
615 log_open();
616
617 r = parse_argv(argc, argv);
618 if (r <= 0)
619 goto finish;
620
621 if (arg_action == ACTION_NEW_ID128) {
622 r = generate_new_id128();
623 goto finish;
624 }
625
626 if (arg_action == ACTION_SETUP_KEYS) {
627 r = setup_keys();
628 goto finish;
629 }
630
631 if (arg_directory)
632 r = sd_journal_open_directory(&j, arg_directory, 0);
633 else
634 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
635
636 if (r < 0) {
637 log_error("Failed to open journal: %s", strerror(-r));
638 goto finish;
639 }
640
641 if (arg_action == ACTION_VERIFY) {
642 r = verify(j);
643 goto finish;
644 }
645
646 if (arg_action == ACTION_PRINT_HEADER) {
647 journal_print_header(j);
648 r = 0;
649 goto finish;
650 }
651
652 #ifdef HAVE_ACL
653 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
654 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
655 #endif
656
657 r = add_this_boot(j);
658 if (r < 0)
659 goto finish;
660
661 r = add_matches(j, argv + optind);
662 if (r < 0)
663 goto finish;
664
665 r = add_priorities(j);
666 if (r < 0)
667 goto finish;
668
669 if (!arg_quiet) {
670 usec_t start, end;
671 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
672
673 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
674 if (r < 0) {
675 log_error("Failed to get cutoff: %s", strerror(-r));
676 goto finish;
677 }
678
679 if (r > 0) {
680 if (arg_follow)
681 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
682 else
683 printf("Logs begin at %s, end at %s.\n",
684 format_timestamp(start_buf, sizeof(start_buf), start),
685 format_timestamp(end_buf, sizeof(end_buf), end));
686 }
687 }
688
689 if (arg_lines >= 0) {
690 r = sd_journal_seek_tail(j);
691 if (r < 0) {
692 log_error("Failed to seek to tail: %s", strerror(-r));
693 goto finish;
694 }
695
696 r = sd_journal_previous_skip(j, arg_lines);
697 } else {
698 r = sd_journal_seek_head(j);
699 if (r < 0) {
700 log_error("Failed to seek to head: %s", strerror(-r));
701 goto finish;
702 }
703
704 r = sd_journal_next(j);
705 }
706
707 if (r < 0) {
708 log_error("Failed to iterate through journal: %s", strerror(-r));
709 goto finish;
710 }
711
712 on_tty();
713 have_pager = !arg_no_pager && !arg_follow && pager_open();
714
715 if (arg_output == OUTPUT_JSON) {
716 fputc('[', stdout);
717 fflush(stdout);
718 }
719
720 for (;;) {
721 for (;;) {
722 sd_id128_t boot_id;
723 int flags =
724 arg_show_all * OUTPUT_SHOW_ALL |
725 have_pager * OUTPUT_FULL_WIDTH |
726 on_tty() * OUTPUT_COLOR;
727
728 if (need_seek) {
729 r = sd_journal_next(j);
730 if (r < 0) {
731 log_error("Failed to iterate through journal: %s", strerror(-r));
732 goto finish;
733 }
734 }
735
736 if (r == 0)
737 break;
738
739 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
740 if (r >= 0) {
741 if (previous_boot_id_valid &&
742 !sd_id128_equal(boot_id, previous_boot_id))
743 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
744
745 previous_boot_id = boot_id;
746 previous_boot_id_valid = true;
747 }
748
749 line ++;
750
751 r = output_journal(j, arg_output, line, 0, flags);
752 if (r < 0)
753 goto finish;
754
755 need_seek = true;
756 }
757
758 if (!arg_follow)
759 break;
760
761 r = sd_journal_wait(j, (uint64_t) -1);
762 if (r < 0) {
763 log_error("Couldn't wait for log event: %s", strerror(-r));
764 goto finish;
765 }
766 }
767
768 if (arg_output == OUTPUT_JSON)
769 fputs("\n]\n", stdout);
770
771 finish:
772 if (j)
773 sd_journal_close(j);
774
775 pager_close();
776
777 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
778 }