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