]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredumpctl.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / journal / coredumpctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
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 <getopt.h>
24 #include <locale.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "sd-journal.h"
30
31 #include "compress.h"
32 #include "fd-util.h"
33 #include "journal-internal.h"
34 #include "log.h"
35 #include "macro.h"
36 #include "pager.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
40 #include "set.h"
41 #include "sigbus.h"
42 #include "signal-util.h"
43 #include "string-util.h"
44 #include "terminal-util.h"
45 #include "user-util.h"
46 #include "util.h"
47
48 static enum {
49 ACTION_NONE,
50 ACTION_INFO,
51 ACTION_LIST,
52 ACTION_DUMP,
53 ACTION_GDB,
54 } arg_action = ACTION_LIST;
55 static const char* arg_field = NULL;
56 static const char *arg_directory = NULL;
57 static int arg_no_pager = false;
58 static int arg_no_legend = false;
59 static int arg_one = false;
60 static FILE* arg_output = NULL;
61
62 static Set *new_matches(void) {
63 Set *set;
64 char *tmp;
65 int r;
66
67 set = set_new(NULL);
68 if (!set) {
69 log_oom();
70 return NULL;
71 }
72
73 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
74 if (!tmp) {
75 log_oom();
76 set_free(set);
77 return NULL;
78 }
79
80 r = set_consume(set, tmp);
81 if (r < 0) {
82 log_error_errno(r, "failed to add to set: %m");
83 set_free(set);
84 return NULL;
85 }
86
87 return set;
88 }
89
90 static int add_match(Set *set, const char *match) {
91 _cleanup_free_ char *p = NULL;
92 char *pattern = NULL;
93 const char* prefix;
94 pid_t pid;
95 int r;
96
97 if (strchr(match, '='))
98 prefix = "";
99 else if (strchr(match, '/')) {
100 r = path_make_absolute_cwd(match, &p);
101 if (r < 0)
102 goto fail;
103 match = p;
104 prefix = "COREDUMP_EXE=";
105 } else if (parse_pid(match, &pid) >= 0)
106 prefix = "COREDUMP_PID=";
107 else
108 prefix = "COREDUMP_COMM=";
109
110 pattern = strjoin(prefix, match, NULL);
111 if (!pattern) {
112 r = -ENOMEM;
113 goto fail;
114 }
115
116 log_debug("Adding pattern: %s", pattern);
117 r = set_consume(set, pattern);
118 if (r < 0)
119 goto fail;
120
121 return 0;
122 fail:
123 return log_error_errno(r, "Failed to add match: %m");
124 }
125
126 static void help(void) {
127 printf("%s [OPTIONS...]\n\n"
128 "List or retrieve coredumps from the journal.\n\n"
129 "Flags:\n"
130 " -h --help Show this help\n"
131 " --version Print version string\n"
132 " --no-pager Do not pipe output into a pager\n"
133 " --no-legend Do not print the column headers.\n"
134 " -1 Show information about most recent entry only\n"
135 " -F --field=FIELD List all values a certain field takes\n"
136 " -o --output=FILE Write output to FILE\n\n"
137 " -D --directory=DIR Use journal files from directory\n\n"
138
139 "Commands:\n"
140 " list [MATCHES...] List available coredumps (default)\n"
141 " info [MATCHES...] Show detailed information about one or more coredumps\n"
142 " dump [MATCHES...] Print first matching coredump to stdout\n"
143 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
144 , program_invocation_short_name);
145 }
146
147 static int parse_argv(int argc, char *argv[], Set *matches) {
148 enum {
149 ARG_VERSION = 0x100,
150 ARG_NO_PAGER,
151 ARG_NO_LEGEND,
152 };
153
154 int r, c;
155
156 static const struct option options[] = {
157 { "help", no_argument, NULL, 'h' },
158 { "version" , no_argument, NULL, ARG_VERSION },
159 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
160 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
161 { "output", required_argument, NULL, 'o' },
162 { "field", required_argument, NULL, 'F' },
163 { "directory", required_argument, NULL, 'D' },
164 {}
165 };
166
167 assert(argc >= 0);
168 assert(argv);
169
170 while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
171 switch(c) {
172
173 case 'h':
174 arg_action = ACTION_NONE;
175 help();
176 return 0;
177
178 case ARG_VERSION:
179 arg_action = ACTION_NONE;
180 return version();
181
182 case ARG_NO_PAGER:
183 arg_no_pager = true;
184 break;
185
186 case ARG_NO_LEGEND:
187 arg_no_legend = true;
188 break;
189
190 case 'o':
191 if (arg_output) {
192 log_error("cannot set output more than once");
193 return -EINVAL;
194 }
195
196 arg_output = fopen(optarg, "we");
197 if (!arg_output)
198 return log_error_errno(errno, "writing to '%s': %m", optarg);
199
200 break;
201
202 case 'F':
203 if (arg_field) {
204 log_error("cannot use --field/-F more than once");
205 return -EINVAL;
206 }
207 arg_field = optarg;
208 break;
209
210 case '1':
211 arg_one = true;
212 break;
213
214 case 'D':
215 arg_directory = optarg;
216 break;
217
218 case '?':
219 return -EINVAL;
220
221 default:
222 assert_not_reached("Unhandled option");
223 }
224
225 if (optind < argc) {
226 const char *cmd = argv[optind++];
227 if (streq(cmd, "list"))
228 arg_action = ACTION_LIST;
229 else if (streq(cmd, "dump"))
230 arg_action = ACTION_DUMP;
231 else if (streq(cmd, "gdb"))
232 arg_action = ACTION_GDB;
233 else if (streq(cmd, "info"))
234 arg_action = ACTION_INFO;
235 else {
236 log_error("Unknown action '%s'", cmd);
237 return -EINVAL;
238 }
239 }
240
241 if (arg_field && arg_action != ACTION_LIST) {
242 log_error("Option --field/-F only makes sense with list");
243 return -EINVAL;
244 }
245
246 while (optind < argc) {
247 r = add_match(matches, argv[optind]);
248 if (r != 0)
249 return r;
250 optind++;
251 }
252
253 return 0;
254 }
255
256 static int retrieve(const void *data,
257 size_t len,
258 const char *name,
259 char **var) {
260
261 size_t ident;
262 char *v;
263
264 ident = strlen(name) + 1; /* name + "=" */
265
266 if (len < ident)
267 return 0;
268
269 if (memcmp(data, name, ident - 1) != 0)
270 return 0;
271
272 if (((const char*) data)[ident - 1] != '=')
273 return 0;
274
275 v = strndup((const char*)data + ident, len - ident);
276 if (!v)
277 return log_oom();
278
279 free(*var);
280 *var = v;
281
282 return 0;
283 }
284
285 static void print_field(FILE* file, sd_journal *j) {
286 _cleanup_free_ char *value = NULL;
287 const void *d;
288 size_t l;
289
290 assert(file);
291 assert(j);
292
293 assert(arg_field);
294
295 SD_JOURNAL_FOREACH_DATA(j, d, l)
296 retrieve(d, l, arg_field, &value);
297
298 if (value)
299 fprintf(file, "%s\n", value);
300 }
301
302 static int print_list(FILE* file, sd_journal *j, int had_legend) {
303 _cleanup_free_ char
304 *pid = NULL, *uid = NULL, *gid = NULL,
305 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
306 *filename = NULL;
307 const void *d;
308 size_t l;
309 usec_t t;
310 char buf[FORMAT_TIMESTAMP_MAX];
311 int r;
312 bool present;
313
314 assert(file);
315 assert(j);
316
317 SD_JOURNAL_FOREACH_DATA(j, d, l) {
318 retrieve(d, l, "COREDUMP_PID", &pid);
319 retrieve(d, l, "COREDUMP_UID", &uid);
320 retrieve(d, l, "COREDUMP_GID", &gid);
321 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
322 retrieve(d, l, "COREDUMP_EXE", &exe);
323 retrieve(d, l, "COREDUMP_COMM", &comm);
324 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
325 retrieve(d, l, "COREDUMP_FILENAME", &filename);
326 }
327
328 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
329 log_warning("Empty coredump log entry");
330 return -EINVAL;
331 }
332
333 r = sd_journal_get_realtime_usec(j, &t);
334 if (r < 0)
335 return log_error_errno(r, "Failed to get realtime timestamp: %m");
336
337 format_timestamp(buf, sizeof(buf), t);
338 present = filename && access(filename, F_OK) == 0;
339
340 if (!had_legend && !arg_no_legend)
341 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
342 FORMAT_TIMESTAMP_WIDTH, "TIME",
343 6, "PID",
344 5, "UID",
345 5, "GID",
346 3, "SIG",
347 1, "PRESENT",
348 "EXE");
349
350 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
351 FORMAT_TIMESTAMP_WIDTH, buf,
352 6, strna(pid),
353 5, strna(uid),
354 5, strna(gid),
355 3, strna(sgnl),
356 1, present ? "*" : "",
357 strna(exe ?: (comm ?: cmdline)));
358
359 return 0;
360 }
361
362 static int print_info(FILE *file, sd_journal *j, bool need_space) {
363 _cleanup_free_ char
364 *pid = NULL, *uid = NULL, *gid = NULL,
365 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
366 *unit = NULL, *user_unit = NULL, *session = NULL,
367 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
368 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
369 *message = NULL, *timestamp = NULL, *filename = NULL;
370 const void *d;
371 size_t l;
372 int r;
373
374 assert(file);
375 assert(j);
376
377 SD_JOURNAL_FOREACH_DATA(j, d, l) {
378 retrieve(d, l, "COREDUMP_PID", &pid);
379 retrieve(d, l, "COREDUMP_UID", &uid);
380 retrieve(d, l, "COREDUMP_GID", &gid);
381 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
382 retrieve(d, l, "COREDUMP_EXE", &exe);
383 retrieve(d, l, "COREDUMP_COMM", &comm);
384 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
385 retrieve(d, l, "COREDUMP_UNIT", &unit);
386 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
387 retrieve(d, l, "COREDUMP_SESSION", &session);
388 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
389 retrieve(d, l, "COREDUMP_SLICE", &slice);
390 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
391 retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
392 retrieve(d, l, "COREDUMP_FILENAME", &filename);
393 retrieve(d, l, "_BOOT_ID", &boot_id);
394 retrieve(d, l, "_MACHINE_ID", &machine_id);
395 retrieve(d, l, "_HOSTNAME", &hostname);
396 retrieve(d, l, "MESSAGE", &message);
397 }
398
399 if (need_space)
400 fputs("\n", file);
401
402 if (comm)
403 fprintf(file,
404 " PID: %s%s%s (%s)\n",
405 ansi_highlight(), strna(pid), ansi_normal(), comm);
406 else
407 fprintf(file,
408 " PID: %s%s%s\n",
409 ansi_highlight(), strna(pid), ansi_normal());
410
411 if (uid) {
412 uid_t n;
413
414 if (parse_uid(uid, &n) >= 0) {
415 _cleanup_free_ char *u = NULL;
416
417 u = uid_to_name(n);
418 fprintf(file,
419 " UID: %s (%s)\n",
420 uid, u);
421 } else {
422 fprintf(file,
423 " UID: %s\n",
424 uid);
425 }
426 }
427
428 if (gid) {
429 gid_t n;
430
431 if (parse_gid(gid, &n) >= 0) {
432 _cleanup_free_ char *g = NULL;
433
434 g = gid_to_name(n);
435 fprintf(file,
436 " GID: %s (%s)\n",
437 gid, g);
438 } else {
439 fprintf(file,
440 " GID: %s\n",
441 gid);
442 }
443 }
444
445 if (sgnl) {
446 int sig;
447
448 if (safe_atoi(sgnl, &sig) >= 0)
449 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
450 else
451 fprintf(file, " Signal: %s\n", sgnl);
452 }
453
454 if (timestamp) {
455 usec_t u;
456
457 r = safe_atou64(timestamp, &u);
458 if (r >= 0) {
459 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
460
461 fprintf(file,
462 " Timestamp: %s (%s)\n",
463 format_timestamp(absolute, sizeof(absolute), u),
464 format_timestamp_relative(relative, sizeof(relative), u));
465
466 } else
467 fprintf(file, " Timestamp: %s\n", timestamp);
468 }
469
470 if (cmdline)
471 fprintf(file, " Command Line: %s\n", cmdline);
472 if (exe)
473 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal());
474 if (cgroup)
475 fprintf(file, " Control Group: %s\n", cgroup);
476 if (unit)
477 fprintf(file, " Unit: %s\n", unit);
478 if (user_unit)
479 fprintf(file, " User Unit: %s\n", unit);
480 if (slice)
481 fprintf(file, " Slice: %s\n", slice);
482 if (session)
483 fprintf(file, " Session: %s\n", session);
484 if (owner_uid) {
485 uid_t n;
486
487 if (parse_uid(owner_uid, &n) >= 0) {
488 _cleanup_free_ char *u = NULL;
489
490 u = uid_to_name(n);
491 fprintf(file,
492 " Owner UID: %s (%s)\n",
493 owner_uid, u);
494 } else {
495 fprintf(file,
496 " Owner UID: %s\n",
497 owner_uid);
498 }
499 }
500 if (boot_id)
501 fprintf(file, " Boot ID: %s\n", boot_id);
502 if (machine_id)
503 fprintf(file, " Machine ID: %s\n", machine_id);
504 if (hostname)
505 fprintf(file, " Hostname: %s\n", hostname);
506
507 if (filename && access(filename, F_OK) == 0)
508 fprintf(file, " Coredump: %s\n", filename);
509
510 if (message) {
511 _cleanup_free_ char *m = NULL;
512
513 m = strreplace(message, "\n", "\n ");
514
515 fprintf(file, " Message: %s\n", strstrip(m ?: message));
516 }
517
518 return 0;
519 }
520
521 static int focus(sd_journal *j) {
522 int r;
523
524 r = sd_journal_seek_tail(j);
525 if (r == 0)
526 r = sd_journal_previous(j);
527 if (r < 0)
528 return log_error_errno(r, "Failed to search journal: %m");
529 if (r == 0) {
530 log_error("No match found.");
531 return -ESRCH;
532 }
533 return r;
534 }
535
536 static void print_entry(sd_journal *j, unsigned n_found) {
537 assert(j);
538
539 if (arg_action == ACTION_INFO)
540 print_info(stdout, j, n_found);
541 else if (arg_field)
542 print_field(stdout, j);
543 else
544 print_list(stdout, j, n_found);
545 }
546
547 static int dump_list(sd_journal *j) {
548 unsigned n_found = 0;
549 int r;
550
551 assert(j);
552
553 /* The coredumps are likely to compressed, and for just
554 * listing them we don't need to decompress them, so let's
555 * pick a fairly low data threshold here */
556 sd_journal_set_data_threshold(j, 4096);
557
558 if (arg_one) {
559 r = focus(j);
560 if (r < 0)
561 return r;
562
563 print_entry(j, 0);
564 } else {
565 SD_JOURNAL_FOREACH(j)
566 print_entry(j, n_found++);
567
568 if (!arg_field && n_found <= 0) {
569 log_notice("No coredumps found.");
570 return -ESRCH;
571 }
572 }
573
574 return 0;
575 }
576
577 static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
578 const char *data;
579 _cleanup_free_ char *filename = NULL;
580 size_t len;
581 int r;
582
583 assert((fd >= 0) != !!path);
584 assert(!!path == !!unlink_temp);
585
586 /* Prefer uncompressed file to journal (probably cached) to
587 * compressed file (probably uncached). */
588 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
589 if (r < 0 && r != -ENOENT)
590 log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
591 else if (r == 0)
592 retrieve(data, len, "COREDUMP_FILENAME", &filename);
593
594 if (filename && access(filename, R_OK) < 0) {
595 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
596 "File %s is not readable: %m", filename);
597 filename = mfree(filename);
598 }
599
600 if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
601 if (path) {
602 *path = filename;
603 filename = NULL;
604 }
605
606 return 0;
607 } else {
608 _cleanup_close_ int fdt = -1;
609 char *temp = NULL;
610
611 if (fd < 0) {
612 temp = strdup("/var/tmp/coredump-XXXXXX");
613 if (!temp)
614 return log_oom();
615
616 fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
617 if (fdt < 0)
618 return log_error_errno(errno, "Failed to create temporary file: %m");
619 log_debug("Created temporary file %s", temp);
620
621 fd = fdt;
622 }
623
624 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
625 if (r == 0) {
626 ssize_t sz;
627
628 assert(len >= 9);
629 data += 9;
630 len -= 9;
631
632 sz = write(fdt, data, len);
633 if (sz < 0) {
634 r = log_error_errno(errno,
635 "Failed to write temporary file: %m");
636 goto error;
637 }
638 if (sz != (ssize_t) len) {
639 log_error("Short write to temporary file.");
640 r = -EIO;
641 goto error;
642 }
643 } else if (filename) {
644 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
645 _cleanup_close_ int fdf;
646
647 fdf = open(filename, O_RDONLY | O_CLOEXEC);
648 if (fdf < 0) {
649 r = log_error_errno(errno,
650 "Failed to open %s: %m",
651 filename);
652 goto error;
653 }
654
655 r = decompress_stream(filename, fdf, fd, -1);
656 if (r < 0) {
657 log_error_errno(r, "Failed to decompress %s: %m", filename);
658 goto error;
659 }
660 #else
661 log_error("Cannot decompress file. Compiled without compression support.");
662 r = -EOPNOTSUPP;
663 goto error;
664 #endif
665 } else {
666 if (r == -ENOENT)
667 log_error("Cannot retrieve coredump from journal nor disk.");
668 else
669 log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
670 goto error;
671 }
672
673 if (temp) {
674 *path = temp;
675 *unlink_temp = true;
676 }
677
678 return 0;
679
680 error:
681 if (temp) {
682 unlink(temp);
683 log_debug("Removed temporary file %s", temp);
684 }
685 return r;
686 }
687 }
688
689 static int dump_core(sd_journal* j) {
690 int r;
691
692 assert(j);
693
694 r = focus(j);
695 if (r < 0)
696 return r;
697
698 print_info(arg_output ? stdout : stderr, j, false);
699
700 if (on_tty() && !arg_output) {
701 log_error("Refusing to dump core to tty.");
702 return -ENOTTY;
703 }
704
705 r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
706 if (r < 0)
707 return log_error_errno(r, "Coredump retrieval failed: %m");
708
709 r = sd_journal_previous(j);
710 if (r >= 0)
711 log_warning("More than one entry matches, ignoring rest.");
712
713 return 0;
714 }
715
716 static int run_gdb(sd_journal *j) {
717 _cleanup_free_ char *exe = NULL, *path = NULL;
718 bool unlink_path = false;
719 const char *data;
720 siginfo_t st;
721 size_t len;
722 pid_t pid;
723 int r;
724
725 assert(j);
726
727 r = focus(j);
728 if (r < 0)
729 return r;
730
731 print_info(stdout, j, false);
732 fputs("\n", stdout);
733
734 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
735 if (r < 0)
736 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
737
738 assert(len > strlen("COREDUMP_EXE="));
739 data += strlen("COREDUMP_EXE=");
740 len -= strlen("COREDUMP_EXE=");
741
742 exe = strndup(data, len);
743 if (!exe)
744 return log_oom();
745
746 if (endswith(exe, " (deleted)")) {
747 log_error("Binary already deleted.");
748 return -ENOENT;
749 }
750
751 if (!path_is_absolute(exe)) {
752 log_error("Binary is not an absolute path.");
753 return -ENOENT;
754 }
755
756 r = save_core(j, -1, &path, &unlink_path);
757 if (r < 0)
758 return log_error_errno(r, "Failed to retrieve core: %m");
759
760 pid = fork();
761 if (pid < 0) {
762 r = log_error_errno(errno, "Failed to fork(): %m");
763 goto finish;
764 }
765 if (pid == 0) {
766 (void) reset_all_signal_handlers();
767 (void) reset_signal_mask();
768
769 execlp("gdb", "gdb", exe, path, NULL);
770
771 log_error_errno(errno, "Failed to invoke gdb: %m");
772 _exit(1);
773 }
774
775 r = wait_for_terminate(pid, &st);
776 if (r < 0) {
777 log_error_errno(errno, "Failed to wait for gdb: %m");
778 goto finish;
779 }
780
781 r = st.si_code == CLD_EXITED ? st.si_status : 255;
782
783 finish:
784 if (unlink_path) {
785 log_debug("Removed temporary file %s", path);
786 unlink(path);
787 }
788
789 return r;
790 }
791
792 int main(int argc, char *argv[]) {
793 _cleanup_journal_close_ sd_journal*j = NULL;
794 const char* match;
795 Iterator it;
796 int r = 0;
797 _cleanup_set_free_free_ Set *matches = NULL;
798
799 setlocale(LC_ALL, "");
800 log_parse_environment();
801 log_open();
802
803 matches = new_matches();
804 if (!matches) {
805 r = -ENOMEM;
806 goto end;
807 }
808
809 r = parse_argv(argc, argv, matches);
810 if (r < 0)
811 goto end;
812
813 if (arg_action == ACTION_NONE)
814 goto end;
815
816 sigbus_install();
817
818 if (arg_directory) {
819 r = sd_journal_open_directory(&j, arg_directory, 0);
820 if (r < 0) {
821 log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
822 goto end;
823 }
824 } else {
825 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
826 if (r < 0) {
827 log_error_errno(r, "Failed to open journal: %m");
828 goto end;
829 }
830 }
831
832 /* We want full data, nothing truncated. */
833 sd_journal_set_data_threshold(j, 0);
834
835 SET_FOREACH(match, matches, it) {
836 r = sd_journal_add_match(j, match, strlen(match));
837 if (r != 0) {
838 log_error_errno(r, "Failed to add match '%s': %m",
839 match);
840 goto end;
841 }
842 }
843
844 if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
845 _cleanup_free_ char *filter;
846
847 filter = journal_make_match_string(j);
848 log_debug("Journal filter: %s", filter);
849 }
850
851 switch(arg_action) {
852
853 case ACTION_LIST:
854 case ACTION_INFO:
855 if (!arg_no_pager)
856 pager_open(false);
857
858 r = dump_list(j);
859 break;
860
861 case ACTION_DUMP:
862 r = dump_core(j);
863 break;
864
865 case ACTION_GDB:
866 r = run_gdb(j);
867 break;
868
869 default:
870 assert_not_reached("Shouldn't be here");
871 }
872
873 end:
874 pager_close();
875
876 if (arg_output)
877 fclose(arg_output);
878
879 return r >= 0 ? r : EXIT_FAILURE;
880 }