]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredumpctl.c
Merge pull request #1039 from poettering/nspawn-machine-template
[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 <locale.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28
29 #include "sd-journal.h"
30 #include "build.h"
31 #include "set.h"
32 #include "util.h"
33 #include "log.h"
34 #include "path-util.h"
35 #include "pager.h"
36 #include "macro.h"
37 #include "journal-internal.h"
38 #include "compress.h"
39 #include "sigbus.h"
40 #include "process-util.h"
41 #include "terminal-util.h"
42 #include "signal-util.h"
43
44 static enum {
45 ACTION_NONE,
46 ACTION_INFO,
47 ACTION_LIST,
48 ACTION_DUMP,
49 ACTION_GDB,
50 } arg_action = ACTION_LIST;
51 static const char* arg_field = NULL;
52 static const char *arg_directory = NULL;
53 static int arg_no_pager = false;
54 static int arg_no_legend = false;
55 static int arg_one = false;
56 static FILE* arg_output = NULL;
57
58 static Set *new_matches(void) {
59 Set *set;
60 char *tmp;
61 int r;
62
63 set = set_new(NULL);
64 if (!set) {
65 log_oom();
66 return NULL;
67 }
68
69 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
70 if (!tmp) {
71 log_oom();
72 set_free(set);
73 return NULL;
74 }
75
76 r = set_consume(set, tmp);
77 if (r < 0) {
78 log_error_errno(r, "failed to add to set: %m");
79 set_free(set);
80 return NULL;
81 }
82
83 return set;
84 }
85
86 static int add_match(Set *set, const char *match) {
87 int r = -ENOMEM;
88 unsigned pid;
89 const char* prefix;
90 char *pattern = NULL;
91 _cleanup_free_ char *p = NULL;
92
93 if (strchr(match, '='))
94 prefix = "";
95 else if (strchr(match, '/')) {
96 p = path_make_absolute_cwd(match);
97 if (!p)
98 goto fail;
99
100 match = p;
101 prefix = "COREDUMP_EXE=";
102 }
103 else if (safe_atou(match, &pid) == 0)
104 prefix = "COREDUMP_PID=";
105 else
106 prefix = "COREDUMP_COMM=";
107
108 pattern = strjoin(prefix, match, NULL);
109 if (!pattern)
110 goto fail;
111
112 log_debug("Adding pattern: %s", pattern);
113 r = set_consume(set, pattern);
114 if (r < 0) {
115 log_error_errno(r, "Failed to add pattern: %m");
116 goto fail;
117 }
118
119 return 0;
120 fail:
121 return log_error_errno(r, "Failed to add match: %m");
122 }
123
124 static void help(void) {
125 printf("%s [OPTIONS...]\n\n"
126 "List or retrieve coredumps from the journal.\n\n"
127 "Flags:\n"
128 " -h --help Show this help\n"
129 " --version Print version string\n"
130 " --no-pager Do not pipe output into a pager\n"
131 " --no-legend Do not print the column headers.\n"
132 " -1 Show information about most recent entry only\n"
133 " -F --field=FIELD List all values a certain field takes\n"
134 " -o --output=FILE Write output to FILE\n\n"
135 " -D --directory=DIR Use journal files from directory\n\n"
136
137 "Commands:\n"
138 " list [MATCHES...] List available coredumps (default)\n"
139 " info [MATCHES...] Show detailed information about one or more coredumps\n"
140 " dump [MATCHES...] Print first matching coredump to stdout\n"
141 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
142 , program_invocation_short_name);
143 }
144
145 static int parse_argv(int argc, char *argv[], Set *matches) {
146 enum {
147 ARG_VERSION = 0x100,
148 ARG_NO_PAGER,
149 ARG_NO_LEGEND,
150 };
151
152 int r, c;
153
154 static const struct option options[] = {
155 { "help", no_argument, NULL, 'h' },
156 { "version" , no_argument, NULL, ARG_VERSION },
157 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
158 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
159 { "output", required_argument, NULL, 'o' },
160 { "field", required_argument, NULL, 'F' },
161 { "directory", required_argument, NULL, 'D' },
162 {}
163 };
164
165 assert(argc >= 0);
166 assert(argv);
167
168 while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
169 switch(c) {
170
171 case 'h':
172 arg_action = ACTION_NONE;
173 help();
174 return 0;
175
176 case ARG_VERSION:
177 arg_action = ACTION_NONE;
178 puts(PACKAGE_STRING);
179 puts(SYSTEMD_FEATURES);
180 return 0;
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_highlight_off(), comm);
406 else
407 fprintf(file,
408 " PID: %s%s%s\n",
409 ansi_highlight(), strna(pid), ansi_highlight_off());
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_highlight_off());
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 log_error_errno(errno, "Failed to write temporary file: %m");
635 r = -errno;
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 log_error_errno(errno, "Failed to open %s: %m", filename);
650 r = -errno;
651 goto error;
652 }
653
654 r = decompress_stream(filename, fdf, fd, -1);
655 if (r < 0) {
656 log_error_errno(r, "Failed to decompress %s: %m", filename);
657 goto error;
658 }
659 #else
660 log_error("Cannot decompress file. Compiled without compression support.");
661 r = -EOPNOTSUPP;
662 goto error;
663 #endif
664 } else {
665 if (r == -ENOENT)
666 log_error("Cannot retrieve coredump from journal nor disk.");
667 else
668 log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
669 goto error;
670 }
671
672 if (temp) {
673 *path = temp;
674 *unlink_temp = true;
675 }
676
677 return 0;
678
679 error:
680 if (temp) {
681 unlink(temp);
682 log_debug("Removed temporary file %s", temp);
683 }
684 return r;
685 }
686 }
687
688 static int dump_core(sd_journal* j) {
689 int r;
690
691 assert(j);
692
693 r = focus(j);
694 if (r < 0)
695 return r;
696
697 print_info(arg_output ? stdout : stderr, j, false);
698
699 if (on_tty() && !arg_output) {
700 log_error("Refusing to dump core to tty.");
701 return -ENOTTY;
702 }
703
704 r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
705 if (r < 0)
706 return log_error_errno(r, "Coredump retrieval failed: %m");
707
708 r = sd_journal_previous(j);
709 if (r >= 0)
710 log_warning("More than one entry matches, ignoring rest.");
711
712 return 0;
713 }
714
715 static int run_gdb(sd_journal *j) {
716 _cleanup_free_ char *exe = NULL, *path = NULL;
717 bool unlink_path = false;
718 const char *data;
719 siginfo_t st;
720 size_t len;
721 pid_t pid;
722 int r;
723
724 assert(j);
725
726 r = focus(j);
727 if (r < 0)
728 return r;
729
730 print_info(stdout, j, false);
731 fputs("\n", stdout);
732
733 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
734 if (r < 0)
735 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
736
737 assert(len > strlen("COREDUMP_EXE="));
738 data += strlen("COREDUMP_EXE=");
739 len -= strlen("COREDUMP_EXE=");
740
741 exe = strndup(data, len);
742 if (!exe)
743 return log_oom();
744
745 if (endswith(exe, " (deleted)")) {
746 log_error("Binary already deleted.");
747 return -ENOENT;
748 }
749
750 if (!path_is_absolute(exe)) {
751 log_error("Binary is not an absolute path.");
752 return -ENOENT;
753 }
754
755 r = save_core(j, -1, &path, &unlink_path);
756 if (r < 0)
757 return log_error_errno(r, "Failed to retrieve core: %m");
758
759 pid = fork();
760 if (pid < 0) {
761 log_error_errno(errno, "Failed to fork(): %m");
762 r = -errno;
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 }