chiark / gitweb /
9eaa8979a012995b25851d26bc0d3e8f74af0167
[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,
407                 *owner_uid = NULL, *message = NULL;
408         const void *d;
409         size_t l;
410
411         assert(file);
412         assert(j);
413
414         SD_JOURNAL_FOREACH_DATA(j, d, l) {
415                 retrieve(d, l, "COREDUMP_PID", &pid);
416                 retrieve(d, l, "COREDUMP_UID", &uid);
417                 retrieve(d, l, "COREDUMP_GID", &gid);
418                 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
419                 retrieve(d, l, "COREDUMP_EXE", &exe);
420                 retrieve(d, l, "COREDUMP_COMM", &comm);
421                 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
422                 retrieve(d, l, "COREDUMP_UNIT", &unit);
423                 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
424                 retrieve(d, l, "COREDUMP_SESSION", &session);
425                 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
426                 retrieve(d, l, "COREDUMP_SLICE", &slice);
427                 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
428                 retrieve(d, l, "_BOOT_ID", &boot_id);
429                 retrieve(d, l, "_MACHINE_ID", &machine_id);
430                 retrieve(d, l, "_HOSTNAME", &hostname);
431                 retrieve(d, l, "MESSAGE", &message);
432         }
433
434         if (need_space)
435                 fputs("\n", file);
436
437         fprintf(file,
438                 "           PID: %s%s%s\n",
439                 ansi_highlight(), strna(pid), ansi_highlight_off());
440
441         if (uid) {
442                 uid_t n;
443
444                 if (parse_uid(uid, &n) >= 0) {
445                         _cleanup_free_ char *u = NULL;
446
447                         u = uid_to_name(n);
448                         fprintf(file,
449                                 "           UID: %s (%s)\n",
450                                 uid, u);
451                 } else {
452                         fprintf(file,
453                                 "           UID: %s\n",
454                                 uid);
455                 }
456         }
457
458         if (gid) {
459                 gid_t n;
460
461                 if (parse_gid(gid, &n) >= 0) {
462                         _cleanup_free_ char *g = NULL;
463
464                         g = gid_to_name(n);
465                         fprintf(file,
466                                 "           GID: %s (%s)\n",
467                                 gid, g);
468                 } else {
469                         fprintf(file,
470                                 "           GID: %s\n",
471                                 gid);
472                 }
473         }
474
475         if (sgnl) {
476                 int sig;
477
478                 if (safe_atoi(sgnl, &sig) >= 0)
479                         fprintf(file, "        Signal: %s (%s)\n", sgnl, signal_to_string(sig));
480                 else
481                         fprintf(file, "        Signal: %s\n", sgnl);
482         }
483
484         if (exe)
485                 fprintf(file, "    Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
486         if (comm)
487                 fprintf(file, "          Comm: %s\n", comm);
488         if (cmdline)
489                 fprintf(file, "  Command Line: %s\n", cmdline);
490         if (cgroup)
491                 fprintf(file, " Control Group: %s\n", cgroup);
492         if (unit)
493                 fprintf(file, "          Unit: %s\n", unit);
494         if (user_unit)
495                 fprintf(file, "     User Unit: %s\n", unit);
496         if (slice)
497                 fprintf(file, "         Slice: %s\n", slice);
498         if (session)
499                 fprintf(file, "       Session: %s\n", session);
500         if (owner_uid) {
501                 uid_t n;
502
503                 if (parse_uid(owner_uid, &n) >= 0) {
504                         _cleanup_free_ char *u = NULL;
505
506                         u = uid_to_name(n);
507                         fprintf(file,
508                                 "     Owner UID: %s (%s)\n",
509                                 owner_uid, u);
510                 } else {
511                         fprintf(file,
512                                 "     Owner UID: %s\n",
513                                 owner_uid);
514                 }
515         }
516         if (boot_id)
517                 fprintf(file, "       Boot ID: %s\n", boot_id);
518         if (machine_id)
519                 fprintf(file, "    Machine ID: %s\n", machine_id);
520         if (hostname)
521                 fprintf(file, "      Hostname: %s\n", hostname);
522
523         if (make_coredump_path(j, &coredump) >= 0)
524                 if (access(coredump, F_OK) >= 0)
525                         fprintf(file, "      Coredump: %s\n", coredump);
526
527         if (message) {
528                 _cleanup_free_ char *m = NULL;
529
530                 m = strreplace(message, "\n", "\n                ");
531
532                 fprintf(file, "       Message: %s\n", strstrip(m ?: message));
533         }
534
535         return 0;
536 }
537
538 static int dump_list(sd_journal *j) {
539         int found = 0;
540
541         assert(j);
542
543         /* The coredumps are likely to compressed, and for just
544          * listing them we don't need to decompress them, so let's
545          * pick a fairly low data threshold here */
546         sd_journal_set_data_threshold(j, 4096);
547
548         SD_JOURNAL_FOREACH(j) {
549                 if (arg_action == ACTION_INFO)
550                         print_info(stdout, j, found++);
551                 else if (arg_field)
552                         print_field(stdout, j);
553                 else
554                         print_list(stdout, j, found++);
555         }
556
557         if (!arg_field && !found) {
558                 log_notice("No coredumps found");
559                 return -ESRCH;
560         }
561
562         return 0;
563 }
564
565 static int focus(sd_journal *j) {
566         int r;
567
568         r = sd_journal_seek_tail(j);
569         if (r == 0)
570                 r = sd_journal_previous(j);
571         if (r < 0) {
572                 log_error("Failed to search journal: %s", strerror(-r));
573                 return r;
574         }
575         if (r == 0) {
576                 log_error("No match found");
577                 return -ESRCH;
578         }
579         return r;
580 }
581
582 static int dump_core(sd_journal* j) {
583         const void *data;
584         size_t len, ret;
585         int r;
586
587         assert(j);
588
589         /* We want full data, nothing truncated. */
590         sd_journal_set_data_threshold(j, 0);
591
592         r = focus(j);
593         if (r < 0)
594                 return r;
595
596         print_info(output ? stdout : stderr, j, false);
597
598         if (on_tty() && !output) {
599                 log_error("Refusing to dump core to tty.");
600                 return -ENOTTY;
601         }
602
603         r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
604         if (r == ENOENT) {
605                 _cleanup_free_ char *fn = NULL;
606                 _cleanup_close_ int fd = -1;
607
608                 r = make_coredump_path(j, &fn);
609                 if (r < 0)
610                         return r;
611
612                 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY);
613                 if (fd < 0) {
614                         if (errno == ENOENT)
615                                 log_error("Coredump neither in journal file nor stored externally on disk.");
616                         else
617                                 log_error("Failed to open coredump file: %s", strerror(-r));
618
619                         return -errno;
620                 }
621
622                 r = copy_bytes(fd, output ? fileno(output) : STDOUT_FILENO);
623                 if (r < 0) {
624                         log_error("Failed to stream coredump: %s", strerror(-r));
625                         return r;
626                 }
627
628         } else if (r < 0) {
629                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
630                 return r;
631
632         } else {
633                 assert(len >= 9);
634                 data = (const uint8_t*) data + 9;
635                 len -= 9;
636
637                 ret = fwrite(data, len, 1, output ?: stdout);
638                 if (ret != 1) {
639                         log_error("Dumping coredump failed: %m (%zu)", ret);
640                         return -errno;
641                 }
642         }
643
644         r = sd_journal_previous(j);
645         if (r >= 0)
646                 log_warning("More than one entry matches, ignoring rest.");
647
648         return 0;
649 }
650
651 static int run_gdb(sd_journal *j) {
652
653         _cleanup_free_ char *exe = NULL, *coredump = NULL;
654         char temp[] = "/var/tmp/coredump-XXXXXX";
655         bool unlink_temp = false;
656         const char *path;
657         const void *data;
658         siginfo_t st;
659         size_t len;
660         pid_t pid;
661         int r;
662
663         assert(j);
664
665         sd_journal_set_data_threshold(j, 0);
666
667         r = focus(j);
668         if (r < 0)
669                 return r;
670
671         print_info(stdout, j, false);
672         fputs("\n", stdout);
673
674         r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
675         if (r < 0) {
676                 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
677                 return r;
678         }
679
680         assert(len >= 13);
681         data = (const uint8_t*) data + 13;
682         len -= 13;
683
684         exe = strndup(data, len);
685         if (!exe)
686                 return log_oom();
687
688         if (endswith(exe, " (deleted)")) {
689                 log_error("Binary already deleted.");
690                 return -ENOENT;
691         }
692
693         if (!path_is_absolute(exe)) {
694                 log_error("Binary is not an absolute path.");
695                 return -ENOENT;
696         }
697
698         r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
699         if (r == -ENOENT) {
700
701                 r = make_coredump_path(j, &coredump);
702                 if (r < 0)
703                         return r;
704
705                 if (access(coredump, R_OK) < 0) {
706                         if (errno == ENOENT)
707                                 log_error("Coredump neither in journal file nor stored externally on disk.");
708                         else
709                                 log_error("Failed to access coredump file: %m");
710
711                         return -errno;
712                 }
713
714                 path = coredump;
715
716         } else if (r < 0) {
717                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
718                 return r;
719
720         } else {
721                 _cleanup_close_ int fd = -1;
722                 ssize_t sz;
723
724                 assert(len >= 9);
725                 data = (const uint8_t*) data + 9;
726                 len -= 9;
727
728                 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
729                 if (fd < 0) {
730                         log_error("Failed to create temporary file: %m");
731                         return -errno;
732                 }
733
734                 unlink_temp = true;
735
736                 sz = write(fd, data, len);
737                 if (sz < 0) {
738                         log_error("Failed to write temporary file: %m");
739                         r = -errno;
740                         goto finish;
741                 }
742                 if (sz != (ssize_t) len) {
743                         log_error("Short write to temporary file.");
744                         r = -EIO;
745                         goto finish;
746                 }
747
748                 path = temp;
749         }
750
751         pid = fork();
752         if (pid < 0) {
753                 log_error("Failed to fork(): %m");
754                 r = -errno;
755                 goto finish;
756         }
757         if (pid == 0) {
758                 execlp("gdb", "gdb", exe, path, NULL);
759
760                 log_error("Failed to invoke gdb: %m");
761                 _exit(1);
762         }
763
764         r = wait_for_terminate(pid, &st);
765         if (r < 0) {
766                 log_error("Failed to wait for gdb: %m");
767                 goto finish;
768         }
769
770         r = st.si_code == CLD_EXITED ? st.si_status : 255;
771
772 finish:
773         if (unlink_temp)
774                 unlink(temp);
775
776         return r;
777 }
778
779 int main(int argc, char *argv[]) {
780         _cleanup_journal_close_ sd_journal*j = NULL;
781         const char* match;
782         Iterator it;
783         int r = 0;
784         _cleanup_set_free_free_ Set *matches = NULL;
785
786         setlocale(LC_ALL, "");
787         log_parse_environment();
788         log_open();
789
790         matches = new_matches();
791         if (!matches) {
792                 r = -ENOMEM;
793                 goto end;
794         }
795
796         r = parse_argv(argc, argv, matches);
797         if (r < 0)
798                 goto end;
799
800         if (arg_action == ACTION_NONE)
801                 goto end;
802
803         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
804         if (r < 0) {
805                 log_error("Failed to open journal: %s", strerror(-r));
806                 goto end;
807         }
808
809         SET_FOREACH(match, matches, it) {
810                 r = sd_journal_add_match(j, match, strlen(match));
811                 if (r != 0) {
812                         log_error("Failed to add match '%s': %s",
813                                   match, strerror(-r));
814                         goto end;
815                 }
816         }
817
818         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
819                 _cleanup_free_ char *filter;
820
821                 filter = journal_make_match_string(j);
822                 log_debug("Journal filter: %s", filter);
823         }
824
825         switch(arg_action) {
826
827         case ACTION_LIST:
828         case ACTION_INFO:
829                 if (!arg_no_pager)
830                         pager_open(false);
831
832                 r = dump_list(j);
833                 break;
834
835         case ACTION_DUMP:
836                 r = dump_core(j);
837                 break;
838
839         case  ACTION_GDB:
840                 r = run_gdb(j);
841                 break;
842
843         default:
844                 assert_not_reached("Shouldn't be here");
845         }
846
847 end:
848         pager_close();
849
850         if (output)
851                 fclose(output);
852
853         return r >= 0 ? r : EXIT_FAILURE;
854 }