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