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